package com.bbcare.followup.plat.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import cn.hutool.core.util.ObjectUtil;
import com.bbcare.followup.plat.store.ibatis.*;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

import com.bbcare.comm.AppException;
import com.bbcare.comm.Constants;
import com.bbcare.comm.DateUtil;
import com.bbcare.comm.ErrorCode;
import com.bbcare.comm.SeqUtil;
import com.bbcare.comm.ToolUtils;
import com.bbcare.department.plat.entity.Contact;
import com.bbcare.department.plat.entity.RandOutPatientSchedule;
import com.bbcare.department.plat.store.ibatis.IContactDao;
import com.bbcare.department.plat.store.ibatis.IOutPatientScheduleDao;
import com.bbcare.followup.plat.entity.FollowQueue;
import com.bbcare.followup.plat.entity.FuInformruleDict;
import com.bbcare.followup.plat.entity.FuLostReason;
import com.bbcare.followup.plat.entity.Patient;
import com.bbcare.followup.plat.entity.PatientContentData;
import com.bbcare.followup.plat.entity.PatientInfoHis;
import com.bbcare.followup.plat.entity.PatientQueueScheme;
import com.bbcare.followup.plat.entity.PatientTask;
import com.bbcare.followup.plat.entity.PatientTaskContent;
import com.bbcare.followup.plat.service.IFollowQueueService;
import com.bbcare.followup.plat.service.IPatientInfoHisService;
import com.bbcare.followup.plat.service.IPatientService;
import com.bbcare.followup.plat.service.IPatientqueryCardService;
import com.bbcare.followup.plat.service.ISmsService;
import com.bbcare.treat.plat.entity.PtTrTask;

/**
 * 患者历史登记服务实现类
 *
 * @author tao
 *
 */
@Service
public class PatientInfoHisService implements IPatientInfoHisService {
    private final Log logger = LogFactory.getLog(PatientInfoHisService.class);

    @Autowired
    private IPatientInfoHisDAO patientInfoHisDao;

    @Autowired
    private IPatientOperationRecordDAO patientOperationRecordDao;

    @Autowired
    private IPatientQueueSchemeDAO patientQueueSchemeDao;

    @Autowired
    private IFollowQueueDAO queueDao;

    @Autowired
    private IFollowQueueService fllowQueueService;

    @Autowired
    private IPatientDAO patientDao;

    // @Autowired
    // private IConfCacheRedis confCacheRedis;

    @Autowired
    private IConfCacheDAO confCacheDao;

    @Autowired
    private IPatientService patientService;

    @Autowired
    private IPatientTaskDAO patientTaskDao;

    @Autowired
    private ISmsTemplateDAO smsTemplDao;

    @Autowired
    private IPtSmsRdDao ptSmsRdDao;

    @Autowired
    private IContactDao deptContactDao;

    @Autowired
    private IOutPatientScheduleDao outPatientScheduleDao;

    // @Autowired
    // private IBizInterAsClient bizInterClient;

    @Resource(name = "jedisPool")
    private JedisPool jedisPool;

    @Autowired
    private IPatientTaskContentDAO patientTaskConDao;

    @Autowired
    private IPatientqueryCardService patientqueryCardService;

    @Autowired
    private ISmsService smsService;

    @Autowired
    private IFollowQueueAuthorityDAO followQueueAuthorityDAO;

    @Override
    public List<PatientInfoHis> getPatientInfoHis(Map<String, Object> paramMap) throws Exception {
        paramMap.put("status", "0");
        return patientInfoHisDao.selectByCondition(paramMap);
    }

    @Override
    @Transactional
    public int patientInfoHisModify(Map<String, Object> paramMap) throws Exception {
        Object id = paramMap.get("id");
        String sysdate = DateUtil.getSysDateA();
        int i = 0;

        Map<String, Object> recordMap = new HashMap<String, Object>();
        if (null == id || StringUtils.isEmpty(String.valueOf(id))) {// 插入
            String seqId = SeqUtil.getSeqNo(Constants.SEQ_NO_FOR_SYSTEMID);
            paramMap.put("updateAccept", seqId);
            paramMap.put("sysdate", sysdate);
            paramMap.put("status", "0");

            // Map<String,Object> recordMap = paramMap;
            i = patientInfoHisDao.savePatientInfoHis(paramMap);

            // 添加记录
            // recordMap.putAll(paramMap);
            paramMap.put("opCode", Constants.OPCODE_ADD);
            paramMap.put("opUsertype", Constants.OPCODE_TYPE_DOCTOR);
            // paramMap.put("userName","大华");//查询
            recordMap = buildOperationRecord(paramMap);
            patientOperationRecordDao.saveOperationRecord(recordMap);
        } else {// 更新
            paramMap.put("updateAccept", id);
            paramMap.put("updateTime", sysdate);
            i = patientInfoHisDao.updatePatientInfoHis(paramMap);
            // 增加操作记录
            paramMap.put("sysdate", sysdate);
            paramMap.put("opCode", Constants.OPCODE_MODIFY);
            paramMap.put("opUsertype", Constants.OPCODE_TYPE_DOCTOR);
            // paramMap.put("userName","大华");//查询
            recordMap = buildOperationRecord(paramMap);
            patientOperationRecordDao.saveOperationRecord(recordMap);
        }
        return i;
    }

    @Override
    public void saveOperationRecord(Map<String, Object> paramMap) throws Exception {
        // Map<String, Object> paramRecord = new HashMap<String, Object>();
        paramMap.put("opTime", DateUtil.getSysDateA());
        paramMap.put("opType", Constants.OP_TYPE_SELF);

        // 根据队列id 查询租户ID
        String queueId = (String) paramMap.get("queueId");
        String tenantId = (String) paramMap.get("tenantId");
        if (!StringUtils.isEmpty(queueId) && StringUtils.isEmpty(tenantId)) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id", queueId);
            List<FollowQueue> queueList = queueDao.selectById(map);
            if (!CollectionUtils.isEmpty(queueList)) {
                tenantId = queueList.get(0).getTenantId();
                paramMap.put("tenantId", tenantId);
            }
        }

        Map<String, Object> outRecord = buildOperationRecord(paramMap);
        patientOperationRecordDao.saveOperationRecord(outRecord);
    }

    @Override
    public Map<String, Object> buildOperationRecord(Map<String, Object> paramMap) throws Exception {
        // 增加操作记录
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        Calendar now = Calendar.getInstance();
        String month = String.valueOf(now.get(Calendar.MONTH) + 1);
        String day = String.valueOf(now.get(Calendar.DAY_OF_MONTH));
        Object opCode = paramMap.get("opCode");

        List<String> inList = new ArrayList<String>();
        String descStr = "";
        String remarkStr = (String) paramMap.get("remark");
        if (StringUtils.isEmpty(remarkStr)) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                jedis.select(Constants.REDIS_DB_INDEX_CONFIG);

                inList.clear();
                inList.add(Constants.CONFIG_BS_STATIC_PARAM_PREFIX_PN);
                inList.add((String) opCode);
                inList.add(Constants.BUSI_OP_CODE_TYPE);
                String strKey = ToolUtils.assembleKey(inList);
                remarkStr = jedis.get(strKey);
                String paramValue = "";
                if (null == remarkStr) {
                    Map<String, Object> inMap = new HashMap<String, Object>();
                    inMap.put("type", Constants.BUSI_OP_CODE_TYPE);
                    inMap.put("paramCode", opCode);
                    List<Map<String, Object>> outList = confCacheDao.selectBsStaticParamByTypeAndCode(inMap);
                    if (null == outList || 0 == outList.size()) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_STATIC_OPCODE_NOT_EXIST, "操作编码未配置!"));
                    }
                    Map<String, Object> outMap = outList.get(0);
                    remarkStr = (String) outMap.get("PARAM_NAME");
                    jedis.set(strKey, remarkStr);

                    inList.clear();
                    inList.add(Constants.CONFIG_BS_STATIC_PARAM_PREFIX_PV);
                    inList.add((String) opCode);
                    inList.add(Constants.BUSI_OP_CODE_TYPE);
                    String paramkey = ToolUtils.assembleKey(inList);
                    paramValue = (String) outMap.get("PARAM_VALUE");
                    jedis.set(paramkey, paramValue);
                }

                StringBuilder desc = new StringBuilder();
                if (Constants.OPCODE_TYPE_DOCTOR.equals(paramMap.get("opUsertype"))) {
                    desc.append("医生");
                    desc.append(paramMap.get("authorName"));
                    desc.append("为患者");
                    desc.append(paramMap.get("userName"));
                } else if (Constants.OPCODE_TYPE_PATIENT.equals(paramMap.get("opUsertype"))) {
                    desc.append("患者");
                    desc.append(paramMap.get("userName"));
                }
                desc.append(paramValue);
                descStr = desc.toString();
            } catch (JedisConnectionException jex) {
                logger.error("redis连接异常..." + jex.getMessage());
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_ERROR, "Redis缓存连接失败!"));
            } finally {
                if (null != jedis) {
                    try {
                        jedis.close();
                        logger.warn("close redis connection...");
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_CLOSE_FAILED,
                                "Redis缓存关闭失败!"));
                    }
                }
            }
        } else {
            descStr = remarkStr;
        }

        paramRecord.put("month", month);
        paramRecord.put("day", day);
        paramRecord.put("userId", paramMap.get("userId"));
        paramRecord.put("queueId", paramMap.get("queueId"));// 前端传入 还是自己去查
        paramRecord.put("opUser", paramMap.get("authorId"));
        paramRecord.put("opTime", paramMap.get("opTime"));
        paramRecord.put("remark", remarkStr);
        paramRecord.put("busiCategory", paramMap.get("type"));
        paramRecord.put("opUsertype", paramMap.get("opUsertype"));
        paramRecord.put("id", paramMap.get("updateAccept"));
        paramRecord.put("opDesc", descStr);
        paramRecord.put("opCode", opCode);
        paramRecord.put("busiId", paramMap.get("busiId"));
        paramRecord.put("tenantId", paramMap.get("tenantId"));
        paramRecord.put("opType", paramMap.get("opType"));
        paramRecord.put("taskId", paramMap.get("taskId"));
        paramRecord.put("breifIntro", paramMap.get("breifIntro"));

        return paramRecord;
    }

    @Override
    public int patientInfoHisDelete(Map<String, Object> paramMap) throws Exception {
        // 修改状态 置为已删除
        paramMap.put("status", "1");
        int i = patientInfoHisDao.updatePatientInfoHis(paramMap);
        // 增加操作记录

        return i;
    }

    @Override
    public List<Map<String, Object>> getActiveInfoList(Map<String, Object> paramMap) throws Exception {
        logger.info("in getActiveInfoList");
        int page = (paramMap.get("page") != null && !StringUtils.isEmpty(paramMap.get("page").toString()))
                ? Integer.parseInt(paramMap.get("page").toString()) : 1;
        int rows = (paramMap.get("rows") != null && !StringUtils.isEmpty(paramMap.get("rows").toString()))
                ? Integer.parseInt(paramMap.get("rows").toString()) : 10;
        Object userNameObject = paramMap.get("userName");
        Object userName = (userNameObject != null && !StringUtils.isEmpty(userNameObject))
                ? "%" + userNameObject.toString() + "%" : userNameObject;

        paramMap.put("userName", userName);
        paramMap.put("start", rows * (page - 1));
        paramMap.put("end", rows);

        List<Map<String, Object>> activeInfoList = new ArrayList<Map<String, Object>>();

        List<String> queueIdArray = new ArrayList<String>();
        String queueId = (String) paramMap.get("queueId");
        if (StringUtils.isEmpty(queueId)) {
            // 传QUEUEID则查询当前队列下动态 不传查询当前科室患者动态
            queueIdArray = fllowQueueService.getQueueArrByAuthority(paramMap);
            if (CollectionUtils.isEmpty(queueIdArray)) {
                logger.warn("该账号无可查看到的患者信息...arg==" + paramMap.toString());
                return activeInfoList;
            }
        } else {
            queueIdArray.add(queueId);
        }
        paramMap.put("queueIdIn", queueIdArray);
        paramMap.put("userStatus", Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL);
        activeInfoList = patientOperationRecordDao.selectByConditon(paramMap);
        if (!CollectionUtils.isEmpty(activeInfoList)) {
            for (int i = 0; i < activeInfoList.size(); i++) {
                String opTime = (activeInfoList.get(i).get("opTime") == null) ? null
                        : String.valueOf(activeInfoList.get(i).get("opTime"));
                if (null != opTime) {
                    // opTime = DateUtil.format(opTime);
                    activeInfoList.get(i).put("opTimeDesc", DateUtil.formatTimedifference(opTime));
                }

                // 孕周转换
                String pregnancy = (activeInfoList.get(i).get("pregnancyDay") == null) ? null
                        : String.valueOf(activeInfoList.get(i).get("pregnancyDay"));
                long correctAge = 0L;
                // 根据生日计算年龄
                String birthday = (activeInfoList.get(i).get("birthday") == null) ? null
                        : String.valueOf(activeInfoList.get(i).get("birthday"));
                if (!StringUtils.isEmpty(pregnancy)) {
                    String str = ToolUtils.getPregnancyStr(Integer.parseInt(pregnancy));
                    activeInfoList.get(i).put("pregnancyDay", str);
                    correctAge = ToolUtils.getCorrect(String.valueOf(activeInfoList.get(i).get("birthday")),
                            Integer.parseInt(pregnancy));

                    Date date = new Date(correctAge);
                    activeInfoList.get(i).put("age", ToolUtils.getAgeByBirthday(DateUtil.getDate_8(date)));

                } else {// 孕周为空 则按正常年龄算
                    activeInfoList.get(i).put("age", ToolUtils.getAgeByBirthday(birthday));
                }

                // 创建时间转换
                activeInfoList.get(i).put("opTime",
                        DateUtil.getTimeStampSec(activeInfoList.get(i).get("opTime").toString()));

            }
        }
        logger.info("out getActiveInfoList");
        return activeInfoList;
    }

    @Override
    public int getActiveInfoCount(Map<String, Object> paramMap) throws Exception {
        /*
         * Object userNameObject = paramMap.get("userName"); Object userName =
         * (userNameObject != null && !StringUtils.isEmpty(userNameObject)) ?
         * "%" + userNameObject.toString() + "%" : userNameObject;
         * paramMap.put("userName", userName);
         *
         * List<String> queueIdArray = new ArrayList<String>(); String queueId =
         * (String) paramMap.get("queueId"); if (StringUtils.isEmpty(queueId)) {
         * queueIdArray = fllowQueueService.getQueueArrByAuthority(paramMap); if
         * (CollectionUtils.isEmpty(queueIdArray)) {
         * logger.warn("该账号无可查看到的患者信息...arg==" + paramMap.toString()); return 0;
         * } } else { queueIdArray.add(queueId); } paramMap.put("queueIdIn",
         * queueIdArray);
         */
        return patientOperationRecordDao.CountByConditon(paramMap);
    }

    @Override
    public List<Map<String, Object>> getActiveDoctorsByUserAndQueue(Map<String, Object> paramMap) throws Exception {
        return patientOperationRecordDao.selectActiveDoctors(paramMap);
    }

    @Override
    public List<Map<String, Object>> getActivePatientTrendStatistics(Map<String, Object> paramMap) throws Exception {
        List<String> queueIdArray = new ArrayList<String>();

        String flag = null == paramMap.get("flag") ? "" : (String) paramMap.get("flag");
        List<Map<String, Object>> rsplistN = new ArrayList<Map<String, Object>>();
        String[] months = DateUtil.getLast6Months();
        String[] days = DateUtil.getLast7days();

        queueIdArray = fllowQueueService.getQueueArrByAuthority(paramMap);
        if (CollectionUtils.isEmpty(queueIdArray)) {
            logger.warn("该账号无可查看到的患者信息...arg==" + paramMap.toString());
            if ("1".equals(flag)) {// 按月
                for (int i = 0; i < months.length; i++) {
                    Map<String, Object> mapTemp = new HashMap<String, Object>();
                    mapTemp.put("count", 0);
                    mapTemp.put("date", months[i]);
                    rsplistN.add(mapTemp);
                }
                return rsplistN;
            } else {
                for (int i = 0; i < days.length; i++) {
                    Map<String, Object> mapTemp = new HashMap<String, Object>();
                    mapTemp.put("count", 0);
                    mapTemp.put("date", days[i]);
                    rsplistN.add(mapTemp);
                }
                return rsplistN;
            }
        }
        paramMap.put("queueIdIn", queueIdArray);
        paramMap.put("userStatus", Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL);// 队列中正常状态下的用户

        List<Map<String, Object>> rsplist = new ArrayList<Map<String, Object>>();

        if ("1".equals(flag)) {// 按月
            // 最近6月 按年月
            rsplist = patientOperationRecordDao.countActivePatientTrendByMonth(paramMap);

            Map<String, Object> mapQry = new HashMap<String, Object>();
            for (int j = 0; j < rsplist.size(); j++) {
                mapQry.put(rsplist.get(j).get("date").toString(), rsplist.get(j).get("count"));
            }
            for (int i = 0; i < months.length; i++) {
                Map<String, Object> mapTemp = new HashMap<String, Object>();
                if (!mapQry.containsKey(months[i])) {
                    mapTemp.put("count", 0);
                    mapTemp.put("date", months[i]);
                    rsplistN.add(mapTemp);
                } else {
                    mapTemp.put("count", mapQry.get(months[i]));
                    mapTemp.put("date", months[i]);
                    rsplistN.add(mapTemp);
                }
            }
            return rsplistN;
        } else {
            // 最近7天 按年月日
            rsplist = patientOperationRecordDao.countActivePatientTrendByDay(paramMap);

            Map<String, Object> mapQry = new HashMap<String, Object>();
            for (int j = 0; j < rsplist.size(); j++) {
                mapQry.put(rsplist.get(j).get("date").toString(), rsplist.get(j).get("count"));
            }
            for (int i = 0; i < days.length; i++) {
                Map<String, Object> mapTemp = new HashMap<String, Object>();
                if (!mapQry.containsKey(days[i])) {
                    mapTemp.put("count", 0);
                    mapTemp.put("date", days[i]);
                    rsplistN.add(mapTemp);
                } else {
                    mapTemp.put("count", mapQry.get(days[i]));
                    mapTemp.put("date", days[i]);
                    rsplistN.add(mapTemp);
                }
            }
            return rsplistN;
        }
    }

    // 失访患者list查询接口实现
    @Override
    public HashMap<String, Object> listPatientLost(Map<String, Object> paramMap) throws Exception {
        HashMap<String, Object> argMap = new HashMap<String, Object>(paramMap);
        String tenantId = (String) argMap.get("tenantId");
        int total = 0;
        HashMap<String, Object> retMap = new HashMap<String, Object>();
        List<Map<String, Object>> pqsList = new ArrayList<Map<String, Object>>();
        retMap.put("total", 0);
        retMap.put("rows", pqsList);

        // 若有服务号码或患者姓名，则先获取患者id串（可能有多个）
        List<String> userIdArray = new ArrayList<String>();
        int qryUserFlag = 0;
        String phoneNo = (String) argMap.get("phoneNo");
        String userName = (String) argMap.get("userName");// 可以是姓氏或姓名
        HashMap<String, Object> qryUserMap = new HashMap<String, Object>();
        if (null != phoneNo && !"".equals(phoneNo)) {
            qryUserMap.put("phoneNo", phoneNo);
            qryUserMap.put("tenantId", tenantId);
            qryUserFlag = 1;
        }
        if (null != userName && !"".equals(userName)) {
            argMap.put("userName", userName + "%");
        }
        if (1 == qryUserFlag) {
            List<Patient> ptList = patientDao.select(qryUserMap);
            if (null == ptList || 0 == ptList.size()) {
                logger.warn("无此患者信息...arg==" + qryUserMap.toString());
                return retMap;
            }
            int ipt = 0;
            for (; ipt < ptList.size(); ipt++) {
                Patient ptVo = ptList.get(ipt);
                // 后续sql中用in条件处理（这里先不考虑返回结果超过1000个）
                userIdArray.add(ptVo.getId());
            }
        }

        // 根据当前登录账号获取，其可以查看的队列id（权限处理比较复杂，这里先简化）
        String authorId = (String) argMap.get("authorId");
        HashMap<String, Object> qryQueueMap = new HashMap<String, Object>();
        qryQueueMap.put("authorId", authorId);
        qryQueueMap.put("queueStatus", Constants.QUEUE_STATUS_NOMAL);// 队列状态正常
        qryQueueMap.put("tenantId", tenantId);
        qryQueueMap.put("queueTypePrivate", Constants.QUEUE_TYPE_PRIVATE);
        qryQueueMap.put("queueTypeTenant", Constants.QUEUE_TYPE_TENANT);
        qryQueueMap.put("queueTypePublic", Constants.QUEUE_TYPE_PUBLIC);

        List<FollowQueue> fqList = null;
        // 1：如果传入队列 则直接用队列查询 2：没有传入队列ID 则根据权限先查询 该用户有哪些队列权限 再查询
        String queueId = (String) argMap.get("queueId");
        if (!StringUtils.isEmpty(queueId)) {
            Map<String, Object> queueMap = new HashMap<String, Object>();
            queueMap.put("id", queueId); // 租户id先写死
            fqList = queueDao.selectById(queueMap);
        } else {
            fqList = queueDao.selectQueueIdByAuthority(qryQueueMap);
            //根据authorId查询此用户拥有哪些队列的权限
            if (ObjectUtil.isNotEmpty(fqList)){
                List<String> queueIds = followQueueAuthorityDAO.getDuiLieByAuthorId(authorId);
                fqList = fqList.stream().filter(item -> {
                    if (queueIds.contains(item.getId())){
                        return true;
                    }else {
                        return false;
                    }
                }).collect(Collectors.toList());
            }
        }

        if (null == fqList || 0 == fqList.size()) {
            logger.warn("该账号无可查看到的队列信息...arg==" + qryQueueMap.toString());
            return retMap;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.select(Constants.REDIS_DB_INDEX_CONFIG);
            List<String> queueIdArray = new ArrayList<String>();
            int fqt = 0;
            for (; fqt < fqList.size(); fqt++) {
                FollowQueue fqVo = fqList.get(fqt);
                // 后续sql中用in条件处理（这里先不考虑返回结果超过1000个）
                queueIdArray.add(fqVo.getId());
            }

            // 获取随访患者list
            argMap.put("queueIdIn", queueIdArray);
            if (0 != userIdArray.size()) {
                argMap.put("userIdIn", userIdArray);
            }
            // 当前账号可以看到的所有患者list（flag=2）
            argMap.put("userStatus", Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL);// 队列中正常状态下的用户
            argMap.put("orderByCreateTime", "1");

            total = patientQueueSchemeDao.countLostPtRdNumByCondition(argMap);
            if (total > 0) {
                pqsList = patientQueueSchemeDao.listLostPtRdNumByCondition(argMap);
                if (null == pqsList || 0 == pqsList.size()) {
                    // 数据被删除或改变了
                    total = 0;
                } else {
                    int pq = 0;
                    String pqsQueueId = "";
                    String patientSchemeId = "";
                    Map<String, Object> qryMapIn = new HashMap<String, Object>();
                    Map<String, Object> qryMapOut = new HashMap<String, Object>();
                    Map<String, Object> ptMapIn = new HashMap<String, Object>();
                    Map<String, Object> reasonMapIn = new HashMap<String, Object>();
                    Map<String, Object> reasonMapOut = new HashMap<String, Object>();
                    // PatientTask ptaskVo = new PatientTask();
                    for (; pq < pqsList.size(); pq++) {
                        Map<String, Object> pqsMap = pqsList.get(pq);

                        // 获取队列名称
                        pqsQueueId = (String) pqsMap.get("followQueueId");
                        qryMapIn.clear();
                        qryMapIn.put("queueId", pqsQueueId);
                        qryMapOut = patientService.getQueueName(qryMapIn, jedis);
                        pqsMap.put("followQueueName", qryMapOut.get("queueName"));

                        // 获取方案下所有任务的完成情况
                        int ptsize = 0;
                        int kk = 0;
                        patientSchemeId = (String) pqsMap.get("patientSchemeId");
                        ptMapIn.clear();
                        ptMapIn.put("ptSchemeId", patientSchemeId);
                        ptMapIn.put("seqNoOrderAsc", "1");
                        List<String> taskTypeInList = new ArrayList<String>();
                        taskTypeInList.add(Constants.TASK_TYPE_SCHEME_SCHEDULE);// 门诊任务
                        taskTypeInList.add(Constants.TASK_TYPE_SCHEME_TEMP);// 线上随访（暂时放开）
                        ptMapIn.put("taskTypeIn", taskTypeInList);
                        List<Map<String, Object>> mapOutList = patientTaskDao.selectTaskStatusByPtSchemeId(ptMapIn);
                        if (CollectionUtils.isEmpty(mapOutList) || MapUtils.isEmpty(mapOutList.get(0))) {
                            // 无任务信息，异常情况
                            ptsize = Integer.parseInt("" + pqsMap.get("totalNum"));
                            StringBuffer strStatus = new StringBuffer();
                            strStatus.append("[");
                            for (kk = 0; kk < ptsize; kk++) {
                                strStatus.append("0,");
                            }
                            String strStr = strStatus.substring(0, strStatus.length() - 1);
                            pqsMap.put("taskStatusArr", strStr + "]");
                        } else {
                            pqsMap.put("taskStatusArr", mapOutList.get(0).get("taskStatusArr"));
                        }

                        reasonMapIn.clear();
                        String reasonId = (String) pqsMap.get("reasonId");
                        String reasonName = "";
                        if (!StringUtils.isEmpty(reasonId)) {
                            reasonMapIn.put("reasonId", reasonId);
                            reasonMapOut = patientService.getLostReason(reasonMapIn, jedis);
                            reasonName = (String) reasonMapOut.get("reasonName");
                        }
                        pqsMap.put("reasonName", reasonName);

                     // 年龄
                        Object birthdayObj = (Object) pqsMap.get("birthday");
                        if (null != birthdayObj) {
                            String birthdayStr = String.valueOf(birthdayObj);
                            if (!StringUtils.isEmpty("" + pqsMap.get("pregnancyDay"))
                                    && !StringUtils.isEmpty(birthdayStr)) {
                                String pregnancy = "" + pqsMap.get("pregnancyDay");
                                long correctAge = ToolUtils.getCorrect(String.valueOf(pqsMap.get("birthday")),
                                        Integer.parseInt(pregnancy));

                                Date date = new Date(correctAge);
                                pqsMap.put("age", ToolUtils.getAgeByBirthday(DateUtil.getDate_8(date)));
                                pqsMap.put("pregnancyWeek",
                                        ToolUtils.getPregnancyStr(Integer.parseInt("" + pqsMap.get("pregnancyDay"))));
                            } else if (!StringUtils.isEmpty(birthdayStr)) {
                                // 孕周为空则按正常年龄算
                                pqsMap.put("age", ToolUtils.getAgeByBirthday("" + pqsMap.get("birthday")));
                            } else {
                                pqsMap.put("age", "");
                            }
                        } else {
                            pqsMap.put("age", "");
                        }

                    }
                }
            }
        } catch (JedisConnectionException jex) {
            logger.error("redis连接异常..." + jex.getMessage());
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_ERROR, "Redis缓存连接失败!"));
        } finally {
            if (null != jedis) {
                try {
                    jedis.close();
                    logger.warn("close redis connection...");
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_CLOSE_FAILED,
                            "Redis缓存关闭失败!"));
                }
            }
        }

        retMap.put("total", total);
        retMap.put("rows", pqsList);

        return retMap;
    }

    // 失访原因list查询接口实现
    @Override
    public Map<String, Object> listLostReason(Map<String, Object> paramMap) throws Exception {
        HashMap<String, Object> retMap = new HashMap<String, Object>();
        List<FuLostReason> pqsList = new ArrayList<FuLostReason>();
        retMap.put("total", 0);
        retMap.put("rows", pqsList);

        String authorId = (String) paramMap.get("authorId");
        String tenantId = (String) paramMap.get("tenantId");
        Map<String, Object> inMap = new HashMap<String, Object>();
        inMap.put("typePrivate", Constants.LOST_REASON_TYPE_PRIVATE);
        inMap.put("typeTenant", Constants.LOST_REASON_TYPE_TENANT);
        inMap.put("typePublic", Constants.LOST_REASON_TYPE_PUBLIC);
        inMap.put("authorId", authorId);
        inMap.put("tenantId", tenantId);

        pqsList = confCacheDao.selectAllLostReason(inMap);
        if (!CollectionUtils.isEmpty(pqsList)) {
            retMap.put("total", pqsList.size());
            retMap.put("rows", pqsList);
        }

        return retMap;
    }

    // 失访患者失访原因登记或修改接口实现
    @Transactional
    public void updatePtLostInfo(Map<String, Object> paramMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(paramMap);

        String authorName = (String) argMap.get("authorName");
        String updateTime = (String) argMap.get("updateTime");
        String authorId = (String) argMap.get("authorId");
        String updateAccept = (String) argMap.get("updateAccept");
        String tenantId = (String) argMap.get("tenantId");
        String opCode = (String) argMap.get("opCode");
        String ptLostId = (String) argMap.get("ptLostId");
        String reasonId = (String) argMap.get("reasonId");
        String userId = (String) argMap.get("userId");
        String queueId = (String) argMap.get("queueId");

        // 入操作流水记录表 ia_operation_record
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        Map<String, Object> ptInMap = new HashMap<String, Object>();
        ptInMap.put("userId", userId);
        ptInMap.put("tenantId", tenantId);
        Patient ptOut = patientDao.selectById(ptInMap);
        if (null == ptOut || "".equals(ptOut.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询无此患者记录！"));
        }
        paramRecord.put("userName", ptOut.getUserName());
        paramRecord.put("userId", userId);
        paramRecord.put("queueId", queueId);
        paramRecord.put("authorId", authorId);
        paramRecord.put("opTime", updateTime);
        paramRecord.put("type", Constants.DOCTOR_ACTIVE_BUSI_TYPE_10);
        paramRecord.put("opUsertype", Constants.OPCODE_TYPE_DOCTOR);
        paramRecord.put("authorName", authorName);
        paramRecord.put("opCode", opCode);
        paramRecord.put("updateAccept", updateAccept);
        paramRecord.put("tenantId", tenantId);
        paramRecord.put("remark", argMap.get("opNote"));
        paramRecord.put("opType", argMap.get("opType"));
        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
        patientOperationRecordDao.saveOperationRecord(outRecord);

        // 更新失访原因id
        Map<String, Object> lostMapIn = new HashMap<String, Object>();
        lostMapIn.put("ptLostId", ptLostId);
        lostMapIn.put("reasonId", reasonId);
        lostMapIn.put("updateTime", updateTime);
        lostMapIn.put("updateAccept", updateAccept);
        patientTaskDao.updatePatientLostInfoById(lostMapIn);

        return;
    }

    // 获取患者窗口期任务list接口实现
    public List<PatientTask> getPatientWindowTaskList(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);
        List<PatientTask> ptWinList = patientTaskDao.listPtWinTask(argMap);
        return ptWinList;
    }

    // 获取根据年龄获取患者list接口实现
    public List<Map<String, Object>> getPatientAgeWinList(Map<String, Object> servParamMap) throws Exception {
        List<Map<String, Object>> outList = new ArrayList<Map<String, Object>>();
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);
        outList = patientQueueSchemeDao.listPtAgeWin(argMap);
        return outList;
    }

    // 按年龄为患者发送随访提醒短信接口实现（安医大马鞍山随访提醒短信特殊处理）
    @Transactional
    public void dealPatientWinSmsSendForAge(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String smsRuleType = (String) argMap.get("ruleType");
        String fuInformid = (String) argMap.get("fuInformid");
        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }
        String ageTaskDate = (String) argMap.get("ageTaskDate");
        String taskType = (String) argMap.get("taskType");

        String type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_21;
        String opUsertype = Constants.OPCODE_TYPE_BG_ADMIN;
        String remark = "";
        String authorName = "";
        String tenantId = (String) argMap.get("tenantId");
        String executeAddress = "";
        String informType = "";
        StringBuffer remarkBuffer = new StringBuffer();

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 一定时间段内是否已经随访过
        Map<String, Object> taskMapIn = new HashMap<String, Object>();
        taskMapIn.put("updateTime", ageTaskDate);
        taskMapIn.put("ptSchemeId", ptSchemeid);
        taskMapIn.put("userId", userId);
        taskMapIn.put("revTaskStatus", Constants.PATIENT_TASK_STATUS_INIT);
        taskMapIn.put("finishDateFlag", "1");
        taskMapIn.put("taskType", taskType);
        taskMapIn.put("revSeqNo", Constants.TASK_SEQ_FOR_INIT);
        taskMapIn.put("seqNoOrderAsc", "1");
        List<PatientTask> ptExiList = patientTaskDao.selectPtTaskForAgeSend(taskMapIn);
        if (!CollectionUtils.isEmpty(ptExiList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_HAS_OUTPATIENT,
                    "该患者在" + ageTaskDate + "之后进行过门诊随访，无需发送提醒短信！"));
        }

        // 查询短信提醒规则配置
        Map<String, Object> ruleMapIn = new HashMap<String, Object>();
        ruleMapIn.put("queueId", queueId);
        if (!StringUtils.isEmpty(informType)) {
            ruleMapIn.put("informType", informType);
        }
        ruleMapIn.put("ruleType", smsRuleType);
        ruleMapIn.put("fuInformid", fuInformid);
        ruleMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        List<FuInformruleDict> informRuleList = confCacheDao.getRuleByQueueId(ruleMapIn);
        if (CollectionUtils.isEmpty(informRuleList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULE_NOT_EXIST,
                    "该队列未配置随访提醒规则或开关未放开，请联系系统管理员！"));
        }
        FuInformruleDict fifdVo = informRuleList.get(0);
        String currDateTime = DateUtil.getSysDateA();
        Map<String, Object> smsMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapOut = new HashMap<String, Object>();
        StringBuffer informTypeBuffer = new StringBuffer();

        smsMapIn.clear();
        smsTemplMapIn.clear();
        smsTemplMapOut.clear();
        smsTemplMapIn.put("id", fifdVo.getSmsTemplid());
        smsTemplMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        smsTemplMapOut = smsTemplDao.selectSmsTemplateById(smsTemplMapIn);
        if (MapUtils.isEmpty(smsTemplMapOut)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_NOT_EXIST,
                    "对应短信模板未配置或开关未放开，请联系系统管理员！"));
        }
        // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
        String informRuleType = fifdVo.getInformType();
        if (Constants.FU_INFORM_RULE_TYPE_APP.equals(informRuleType)) {
            // app提醒
            informTypeBuffer.append("app通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(informRuleType)) {
            // 短信提醒
            informTypeBuffer.append("短信通知、");
            float cycleTime = 0;
            if (Constants.OP_CODE_3002.equals(opCode)) {
                cycleTime = fifdVo.getCycleTime();// 单位：小时，后台自动发送短信规则，如一周发送一次
            } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
                cycleTime = fifdVo.getOutcycleTime();// 单位：小时，医生门诊发送短信规则，如半小时发送一次
            }
            String rangeTime = DateUtil.calcDayByHour(currDateTime, Constants.SUBTRACT_CHAR, cycleTime);
            smsMapIn.put("ptSchemeId", ptSchemeid);
            smsMapIn.put("rangeTime", rangeTime);
            smsMapIn.put("smsType", smsTemplMapOut.get("templateType"));
            // smsMapIn.put("sendStatus",
            // Constants.PT_INFORM_SMS_STATUS_SUCCESS);
            // 校验当前周期内是否有已经发了提醒短信
            int cnt = ptSmsRdDao.countSmsRdByPtSchemeId(smsMapIn);
            if (cnt > 0) {
                if (cycleTime * Constants.ONE_HOUR_MINUTE == Constants.FU_INFORM_TIME_INTERVAL_HALF_HOUR) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "提醒短信发送过于密集，请于半小时后再试！"));
                } else if ((int) cycleTime >= Constants.ONE_DAY_HOUR) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "当前周期内已发送过随访提醒短信！"));
                } else {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "提醒短信发送过于密集，请于" + (int) cycleTime + "小时后再试！"));
                }
            }

            // 校验最大发送次数(当前窗口期内允许发送的最大提醒次数)
            if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                smsMapIn.remove("rangeTime");
                cnt = ptSmsRdDao.countSmsRdByPtSchemeId(smsMapIn);
                if (cnt >= fifdVo.getLimitNum()) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                            "患者随访短信已超过最大发送次数，不允许再发送！"));
                }
            }

            // 替换短信模板中的变量
            StringBuilder smsContent = new StringBuilder();
            String smsTempl = (String) smsTemplMapOut.get("smsTemplate");
            if (StringUtils.isEmpty(smsTempl)) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
            }
            if (StringUtils.isEmpty(smsTemplMapOut.get("sign").toString())) {
                // 没定义签名 用默认 或者不签名
                smsContent.append("");
            } else {
                smsContent.append(smsTemplMapOut.get("sign").toString());
            }
            smsContent.append(smsTempl);
            /*
             * 短信模板样例： 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您到本院进行门诊随访${ followAddress
             * }。体检时间：周一至周五（上午：8:00-12:00；下午：2:30-5:30）
             * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
             */
            String smsText = smsContent.toString();
            if (Constants.OP_CODE_3002.equals(opCode) && -1 != smsText.indexOf("${followAddress}")) {
                logger.warn("-------followAddress------");
                String deptId = "";
                Map<String, Object> queueMapIn = new HashMap<String, Object>();
                queueMapIn.put("id", queueId);
                List<FollowQueue> fqList = queueDao.selectById(queueMapIn);
                if (!CollectionUtils.isEmpty(fqList)) {
                    FollowQueue fqVo = fqList.get(0);
                    deptId = fqVo.getDeptId();
                    if (StringUtils.isEmpty(deptId)) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_FOR_DEPT_NOT_EXIST,
                                "队列对应科室id为空!"));
                    }
                } else {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_NOT_EXIST, "任务对应队列不存在!"));
                }
                // 从科室配置表中获取地址，若有多个地址，则不选用(因为不清楚医生会在哪个地址门诊)
                Map<String, Object> deptConMap = new HashMap<String, Object>();
                deptConMap.put("deptId", deptId);
                List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
                if (!CollectionUtils.isEmpty(dcList)) {
                    if (1 == dcList.size()) {
                        executeAddress = dcList.get(0).getAddress();
                    }
                }
                if (StringUtils.isEmpty(executeAddress)) {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                } else {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
                }
            }

            // 调用通用短信接口
            String phoneNo = ptVo.getPhoneNo();
            String smsType = (String) smsTemplMapOut.get("templateType");
            Map<String, Object> msgMapIn = new HashMap<String, Object>();
            msgMapIn.put("authorId", authorId);
            msgMapIn.put("updateTime", updateTime);
            msgMapIn.put("phoneNo", phoneNo);
            msgMapIn.put("ptSchemeid", ptSchemeid);
            msgMapIn.put("queueId", queueId);
            msgMapIn.put("taskId", taskId);
            msgMapIn.put("updateAccept", updateAccept);
            msgMapIn.put("userId", userId);
            msgMapIn.put("ruleType", informRuleType);
            msgMapIn.put("taskName", taskName);
            // 改为调用内部接口
            HashMap<String, Object> outMsgMap = patientqueryCardService.addsendPromptSmsImpl(phoneNo, smsType, smsText,
                    msgMapIn);
            logger.warn("短信发送结果：" + outMsgMap.toString());
        } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(informRuleType)) {
            // 邮件提醒
            informTypeBuffer.append("邮件通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(informRuleType)) {
            // 微信提醒
            informTypeBuffer.append("微信通知、");
        } else {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
        }

        String infmsg = informTypeBuffer.toString().substring(0, informTypeBuffer.length() - 1);

        // 操作日志记录（ia_operation_record）
        if (Constants.OP_CODE_3002.equals(opCode)) {
            // 后台任务处理
            remarkBuffer.append("后台为患者【");
            remarkBuffer.append(ptVo.getUserName());
            remarkBuffer.append("】发送【");
            remarkBuffer.append(taskName);
            remarkBuffer.append("】门诊任务【");
            remarkBuffer.append(infmsg);
            remarkBuffer.append("】提醒");
            remark = remarkBuffer.toString();
        }
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        paramRecord.put("userName", ptVo.getUserName());
        paramRecord.put("userId", userId);
        paramRecord.put("queueId", queueId);
        paramRecord.put("authorId", authorId);
        paramRecord.put("opTime", updateTime);
        paramRecord.put("type", type);
        paramRecord.put("opUsertype", opUsertype);
        paramRecord.put("authorName", authorName);
        paramRecord.put("opCode", opCode);
        paramRecord.put("updateAccept", updateAccept);
        paramRecord.put("tenantId", tenantId);
        paramRecord.put("remark", remark);
        paramRecord.put("opType", opType);
        paramRecord.put("taskId", taskId);
        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
        patientOperationRecordDao.saveOperationRecord(outRecord);

        return;
    }

    // 获取患者窗口期任务list接口实现 新短信下发方案
    public List<PatientTask> getPatientWindowTaskListNew(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);
        List<PatientTask> ptWinList = patientTaskDao.listPtWinTaskNew(argMap);
        return ptWinList;
    }

    // 获取根据年龄获取患者list接口实现 新短信下发方案
    public List<Map<String, Object>> getPatientAgeWinListNew(Map<String, Object> servParamMap) throws Exception {
        List<Map<String, Object>> outList = new ArrayList<Map<String, Object>>();
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);
        outList = patientQueueSchemeDao.listPtAgeWinNew(argMap);
        return outList;
    }

    // 按年龄为患者发送随访提醒短信接口实现（安医大马鞍山随访提醒短信特殊处理） 短信入实时发送表
    @Transactional
    public void dealPatientWinSmsSendForAgeNew(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String smsRuleType = (String) argMap.get("ruleType");
        String fuInformid = (String) argMap.get("ruleId");
        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }
        String ageTaskDate = (String) argMap.get("ageTaskDate");
        String taskType = (String) argMap.get("taskType");

        String type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_21;
        String opUsertype = Constants.OPCODE_TYPE_BG_ADMIN;
        String remark = "";
        String authorName = "";
        String tenantId = (String) argMap.get("tenantId");
        String executeAddress = "";
        String informType = "";
        StringBuffer remarkBuffer = new StringBuffer();

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 一定时间段内是否已经随访过
        Map<String, Object> taskMapIn = new HashMap<String, Object>();
        taskMapIn.put("updateTime", ageTaskDate);
        taskMapIn.put("ptSchemeId", ptSchemeid);
        taskMapIn.put("userId", userId);
        taskMapIn.put("revTaskStatus", Constants.PATIENT_TASK_STATUS_INIT);
        taskMapIn.put("finishDateFlag", "1");
        taskMapIn.put("taskType", taskType);
        taskMapIn.put("revSeqNo", Constants.TASK_SEQ_FOR_INIT);
        taskMapIn.put("seqNoOrderAsc", "1");
        List<PatientTask> ptExiList = patientTaskDao.selectPtTaskForAgeSend(taskMapIn);
        if (!CollectionUtils.isEmpty(ptExiList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_HAS_OUTPATIENT,
                    "该患者在" + ageTaskDate + "之后进行过门诊随访，无需发送提醒短信！"));
        }

        // 查询短信提醒规则配置
        Map<String, Object> ruleMapIn = new HashMap<String, Object>();
        ruleMapIn.put("queueId", queueId);
        if (!StringUtils.isEmpty(informType)) {
            ruleMapIn.put("informType", informType);
        }
        ruleMapIn.put("ruleType", smsRuleType);
        ruleMapIn.put("fuInformid", fuInformid);
        ruleMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        List<FuInformruleDict> informRuleList = confCacheDao.getRuleByQueueId(ruleMapIn);
        if (CollectionUtils.isEmpty(informRuleList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULE_NOT_EXIST,
                    "该队列未配置随访提醒规则或开关未放开，请联系系统管理员！"));
        }
        FuInformruleDict fifdVo = informRuleList.get(0);
        String currDateTime = DateUtil.getSysDateA();
        Map<String, Object> smsMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapOut = new HashMap<String, Object>();
        StringBuffer informTypeBuffer = new StringBuffer();

        smsMapIn.clear();
        smsTemplMapIn.clear();
        smsTemplMapOut.clear();
        smsTemplMapIn.put("id", fifdVo.getSmsTemplid());
        smsTemplMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        smsTemplMapOut = smsTemplDao.selectSmsTemplateById(smsTemplMapIn);
        if (MapUtils.isEmpty(smsTemplMapOut)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_NOT_EXIST,
                    "对应短信模板未配置或开关未放开，请联系系统管理员！"));
        }
        // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
        String informRuleType = fifdVo.getInformType();
        if (Constants.FU_INFORM_RULE_TYPE_APP.equals(informRuleType)) {
            // app提醒
            informTypeBuffer.append("app通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(informRuleType)) {
            // 短信提醒
            informTypeBuffer.append("短信通知、");
            float cycleTime = 0;
            if (Constants.OP_CODE_3002.equals(opCode)) {
                cycleTime = fifdVo.getCycleTime();// 单位：小时，后台自动发送短信规则，如一周发送一次
            } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
                cycleTime = fifdVo.getOutcycleTime();// 单位：小时，医生门诊发送短信规则，如半小时发送一次
            }
            String rangeTime = DateUtil.calcDayByHour(currDateTime, Constants.SUBTRACT_CHAR, cycleTime);
            smsMapIn.put("ptSchemeId", ptSchemeid);
            smsMapIn.put("rangeTime", rangeTime);
            smsMapIn.put("smsType", smsTemplMapOut.get("templateType"));
//            smsMapIn.put("sendStatus",Constants.PT_INFORM_SMS_STATUS_SUCCESS);
            // 校验当前周期内是否有已经发了提醒短信
            int cnt = ptSmsRdDao.countSmsRdByPtSchemeIdAge(smsMapIn);
            if (cnt > 0) {
                if (cycleTime * Constants.ONE_HOUR_MINUTE == Constants.FU_INFORM_TIME_INTERVAL_HALF_HOUR) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "提醒短信发送过于密集，请于 半小时后再试！"));
                } else if ((int) cycleTime >= Constants.ONE_DAY_HOUR) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "当前周期内已发送过随访提醒短信！"));
                } else {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "提醒短信发送过于密集，请于" + (int) cycleTime + "小时后再试！"));
                }
            }

            // 校验最大发送次数(当前窗口期内允许发送的最大提醒次数)
            if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                smsMapIn.remove("rangeTime");
                cnt = ptSmsRdDao.countSmsRdByPtSchemeIdAge(smsMapIn);
                if (cnt >= fifdVo.getLimitNum()) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                            "患者随访短信已超过最大发送次数，不允许再发送！"));
                }
            }

            // 替换短信模板中的变量
            StringBuilder smsContent = new StringBuilder();
            String smsTempl = (String) smsTemplMapOut.get("smsTemplate");
            if (StringUtils.isEmpty(smsTempl)) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
            }
            if (StringUtils.isEmpty(smsTemplMapOut.get("sign").toString())) {
                // 没定义签名 用默认 或者不签名
                smsContent.append("");
            } else {
                smsContent.append(smsTemplMapOut.get("sign").toString());
            }
            smsContent.append(smsTempl);

            /*
             * 短信模板样例： 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您到本院进行门诊随访${ followAddress
             * }。体检时间：周一至周五（上午：8:00-12:00；下午：2:30-5:30）
             * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
             */
            String smsText = smsContent.toString();
            if (Constants.OP_CODE_3002.equals(opCode) && -1 != smsText.indexOf("${followAddress}")) {
                logger.warn("-------followAddress------");
                String deptId = "";
                Map<String, Object> queueMapIn = new HashMap<String, Object>();
                queueMapIn.put("id", queueId);
                List<FollowQueue> fqList = queueDao.selectById(queueMapIn);
                if (!CollectionUtils.isEmpty(fqList)) {
                    FollowQueue fqVo = fqList.get(0);
                    deptId = fqVo.getDeptId();
                    if (StringUtils.isEmpty(deptId)) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_FOR_DEPT_NOT_EXIST,
                                "队列对应科室id为空!"));
                    }
                } else {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_NOT_EXIST, "任务对应队列不存在!"));
                }
                // 从科室配置表中获取地址，若有多个地址，则不选用(因为不清楚医生会在哪个地址门诊)
                Map<String, Object> deptConMap = new HashMap<String, Object>();
                deptConMap.put("deptId", deptId);
                List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
                if (!CollectionUtils.isEmpty(dcList)) {
                    if (1 == dcList.size()) {
                        executeAddress = dcList.get(0).getAddress();
                    }
                }
                if (StringUtils.isEmpty(executeAddress)) {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                } else {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
                }
            }

            // 调用通用短信接口
            String phoneNo = ptVo.getPhoneNo();
            String smsType = (String) smsTemplMapOut.get("templateType");
            Map<String, Object> msgMapIn = new HashMap<String, Object>();
            msgMapIn.put("authorId", authorId);
            msgMapIn.put("updateTime", updateTime);
            msgMapIn.put("phoneNo", phoneNo);
            msgMapIn.put("ptSchemeid", ptSchemeid);
            msgMapIn.put("queueId", queueId);
            msgMapIn.put("taskId", taskId);
            msgMapIn.put("updateAccept", updateAccept);
            msgMapIn.put("userId", userId);
            msgMapIn.put("ruleType", informRuleType);
            msgMapIn.put("tenantId",tenantId);
            msgMapIn.put("taskName", taskName);

            msgMapIn.put("ruleId",fuInformid);
            msgMapIn.put("smsType", smsType);
            msgMapIn.put("smsText", smsText);
            // 改为调用内部接口
            smsService.saveSmsInfo(msgMapIn);
            logger.warn("短信入实时发送表成功");
        } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(informRuleType)) {
            // 邮件提醒
            informTypeBuffer.append("邮件通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(informRuleType)) {
            // 微信提醒
            informTypeBuffer.append("微信通知、");
        } else {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
        }

	     try{
	        String infmsg = informTypeBuffer.toString().substring(0, informTypeBuffer.length() - 1);

	        // 操作日志记录（ia_operation_record）
	        if (Constants.OP_CODE_3002.equals(opCode)) {
	            // 后台任务处理
	            remarkBuffer.append("后台为患者【");
	            remarkBuffer.append(ptVo.getUserName());
	            remarkBuffer.append("】发送【");
	            remarkBuffer.append(taskName);
	            remarkBuffer.append("】门诊任务【");
	            remarkBuffer.append(infmsg);
	            remarkBuffer.append("】提醒");
	            remark = remarkBuffer.toString();
	        }
	        Map<String, Object> paramRecord = new HashMap<String, Object>();
	        paramRecord.put("userName", ptVo.getUserName());
	        paramRecord.put("userId", userId);
	        paramRecord.put("queueId", queueId);
	        paramRecord.put("authorId", authorId);
	        paramRecord.put("opTime", updateTime);
	        paramRecord.put("type", type);
	        paramRecord.put("opUsertype", opUsertype);
	        paramRecord.put("authorName", authorName);
	        paramRecord.put("opCode", opCode);
	        paramRecord.put("updateAccept", updateAccept);
	        paramRecord.put("tenantId", tenantId);
	        paramRecord.put("remark", remark);
	        paramRecord.put("opType", opType);
	        paramRecord.put("taskId", taskId);
	        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
	        patientOperationRecordDao.saveOperationRecord(outRecord);
	    }catch(Exception e){
	    	logger.error("后台发送短信 操作历史入表失败 taskId="+taskId, e);
	    }
	        return;
	    }

    // 窗口期任务患者发送随访提醒短信接口实现
    @Transactional
    public void dealPatientWinTaskSmsSend(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String tenantId = (String) argMap.get("tenantId");
        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }

        String type = "";
        String opUsertype = "";
        String remark = "";
        String authorName = "";
        String executeAddress = "";
        String executeTime = "";
        String informType = "";
        StringBuffer remarkBuffer = new StringBuffer();
        if (Constants.OP_CODE_3002.equals(opCode)) {
            // 后台任务处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_21;
            opUsertype = Constants.OPCODE_TYPE_BG_ADMIN;
        } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
            // 前台门诊处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_10;
            opUsertype = Constants.OPCODE_TYPE_DOCTOR;
            remark = (String) argMap.get("opNote");
            authorName = (String) argMap.get("authorName");
            executeAddress = (String) argMap.get("orderAddress");
            executeTime = (String) argMap.get("orderDate");
            informType = (String) argMap.get("informType");
        }

        Map<String, Object> taskMap = new HashMap<String, Object>();
        // 先锁表，防止并发更新
        taskMap.put("updateAuthor", authorId);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        taskMap.put("taskId", taskId);
        int upret = patientTaskDao.updatePatientTaskById(taskMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 校验患者队列状态，非正常不发送短信
        Map<String, Object> userMap = new HashMap<String, Object>();
        userMap.put("userId", userId);
        List<PatientQueueScheme> patientQueueSchemes = patientQueueSchemeDao.selectByUserId(userMap);
        if (!Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL.equals(patientQueueSchemes.get(0).getUserStatus())) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_STATUS_NOT_NORMAL,
                    "患者在该队列中状态异常，无法进行相关操作！");
        }

        // 校验任务状态
        List<PatientTask> ptList = patientTaskDao.selectById(taskMap);
        if (CollectionUtils.isEmpty(ptList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }
        PatientTask ptask = ptList.get(0);
        String taskStatus = ptask.getTaskStatus();
        if (!Constants.PATIENT_TASK_STATUS_INIT.equals(taskStatus)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASKSTATUS_NOT_INIT,
                    "患者任务状态非未随访，不需要发送随访短信！"));
        }

        // 查询短信提醒规则配置
        Map<String, Object> ruleMapIn = new HashMap<String, Object>();
        ruleMapIn.put("queueId", queueId);
        ruleMapIn.put("ruleType", Constants.FU_INFORM_RULE_SMS_SEND_RULE_TYPE_WIN);// 窗口期任务类型
        if (!StringUtils.isEmpty(informType)) {
            ruleMapIn.put("informType", informType);
        }
        ruleMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        List<FuInformruleDict> informRuleList = confCacheDao.getRuleByQueueId(ruleMapIn);
        if (CollectionUtils.isEmpty(informRuleList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULE_NOT_EXIST,
                    "该队列未配置随访提醒规则或开关未放开，请联系系统管理员！"));
        }
        FuInformruleDict fifdVo = new FuInformruleDict();
        int rulesize = informRuleList.size();
        int pp = 0;
        String currDateTime = DateUtil.getSysDateA();
        Map<String, Object> smsMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapOut = new HashMap<String, Object>();
        StringBuffer informTypeBuffer = new StringBuffer();
        for (pp = 0; pp < rulesize; pp++) {
            fifdVo = informRuleList.get(pp);

            smsMapIn.clear();
            smsTemplMapIn.clear();
            smsTemplMapOut.clear();
            smsTemplMapIn.put("id", fifdVo.getSmsTemplid());
            smsTemplMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
            smsTemplMapOut = smsTemplDao.selectSmsTemplateById(smsTemplMapIn);
            if (null == smsTemplMapOut || 0 == smsTemplMapOut.size()) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_NOT_EXIST,
                        "对应短信模板未配置或开关未放开，请联系系统管理员！"));
            }
            // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
            String ruleType = fifdVo.getInformType();
            if (Constants.FU_INFORM_RULE_TYPE_APP.equals(ruleType)) {
                // app提醒
                informTypeBuffer.append("app通知、");
            } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(ruleType)) {
                // 短信提醒
                informTypeBuffer.append("短信通知、");
                float cycleTime = 0;
                if (Constants.OP_CODE_3002.equals(opCode)) {
                    cycleTime = fifdVo.getCycleTime();// 单位：小时，后台自动发送短信规则，如一周发送一次
                } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
                    cycleTime = fifdVo.getOutcycleTime();// 单位：小时，医生门诊发送短信规则，如半小时发送一次
                }
                String rangeTime = DateUtil.calcDayByHour(currDateTime, Constants.SUBTRACT_CHAR, cycleTime);
                smsMapIn.put("taskId", taskId);
                smsMapIn.put("rangeTime", rangeTime);
                smsMapIn.put("smsType", smsTemplMapOut.get("templateType"));
                smsMapIn.put("sendStatus", Constants.PT_INFORM_SMS_STATUS_SUCCESS);
                // 校验当前周期内是否有已经发了提醒短信
                int cnt = ptSmsRdDao.countSmsRdByCondition(smsMapIn);
                if (cnt > 0) {
                    if (cycleTime * Constants.ONE_HOUR_MINUTE == Constants.FU_INFORM_TIME_INTERVAL_HALF_HOUR) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "提醒短信发送过于密集，请于半小时后再试！"));
                    } else if ((int) cycleTime >= Constants.ONE_DAY_HOUR) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "当前周期内已发送过随访提醒短信！"));
                    } else {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "提醒短信发送过于密集，请于" + (int) cycleTime + "小时后再试！"));
                    }
                }

                // 校验最大发送次数(当前窗口期内允许发送的最大提醒次数)
                if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                    if (ptask.getSmsNum() >= fifdVo.getLimitNum()) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                                "患者当前窗口期内随访短信已超过最大发送次数，不允许再发送！"));
                    }
                }

                // 替换短信模板中的变量
                StringBuilder smsContent = new StringBuilder();
                String smsTempl = (String) smsTemplMapOut.get("smsTemplate");
                if (StringUtils.isEmpty(smsTempl)) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
                }
                if (StringUtils.isEmpty(smsTemplMapOut.get("sign").toString())) {
                    // 没定义签名 用默认 或者不签名
                    smsContent.append("");
                } else {
                    smsContent.append(smsTemplMapOut.get("sign").toString());
                }
                smsContent.append(smsTempl);
                /*
                 * 短信模板样例：
                 * 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您于${followTime}到本院进行门诊随访${
                 * followAddress }。
                 * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
                 */
                String smsText = smsContent.toString();
                if (Constants.OP_CODE_3002.equals(opCode)) {
                    String deptId = "";
                    Map<String, Object> queueMapIn = new HashMap<String, Object>();
                    queueMapIn.put("id", queueId);
                    List<FollowQueue> fqList = queueDao.selectById(queueMapIn);
                    if (!CollectionUtils.isEmpty(fqList)) {
                        FollowQueue fqVo = fqList.get(0);
                        deptId = fqVo.getDeptId();
                        if (StringUtils.isEmpty(deptId)) {
                            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_FOR_DEPT_NOT_EXIST,
                                    "队列对应科室id为空!"));
                        }
                    } else {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_NOT_EXIST, "任务对应队列不存在!"));
                    }
                    if (!StringUtils.isEmpty(ptask.getExecuteAddress())) {
                        executeAddress = ptask.getExecuteAddress();
                        executeTime = ptask.getExecuteTime();
                    } else {
                        // 从科室配置表中获取地址，若有多个地址，则不选用(因为不清楚医生会在哪个地址门诊)
                        Map<String, Object> deptConMap = new HashMap<String, Object>();
                        deptConMap.put("deptId", deptId);
                        List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
                        if (!CollectionUtils.isEmpty(dcList)) {
                            if (1 == dcList.size()) {
                                executeAddress = dcList.get(0).getAddress();
                            }
                        }
                    }

                    // 若执行时间不为空，则判断当前时间是否小于执行时间，小于则使用，否则不使用;
                    String nowDate = DateUtil.getCurrDate2();
                    if (!StringUtils.isEmpty(executeTime)) {
                        if (nowDate.compareTo(executeTime) >= 0) {
                            executeTime = "";
                        }
                    }
                    // 重新查询排班（从明天开始），获取具体时间段(上午，下午或晚上等）
                    Map<String, Object> scheMapIn = new HashMap<String, Object>();
                    scheMapIn.put("deptId", deptId);
                    scheMapIn.put("beginTime", DateUtil.getCurrDateByDay2(1, Constants.PLUS_CHAR));
                    scheMapIn.put("endTime", ptask.getEndTime());
                    List<String> scheDateList = listOutPtRandSchedule(scheMapIn);
                    if (CollectionUtils.isEmpty(scheDateList)) {
                        throw (new AppException(ErrorCode.DEPARTMENT_EXCEPTION_TYPE_OUTPATIENT_SCHEDULE_ISNULL,
                                "截止到任务临期时间，无随访排班！"));
                    }
                    // 判断执行时间是否是排班中的一个
                    if (!StringUtils.isEmpty(executeTime)) {
                        int kt = 0;
                        int ksize = scheDateList.size();
                        String nextDate = "";
                        int flag = 0;
                        for (kt = 0; kt < ksize; kt++) {
                            nextDate = scheDateList.get(kt);
                            String[] nextDateArr = nextDate.split(Constants.COLON_SPLIT_STR);
                            if (executeTime.equals(nextDateArr[0])) {
                                executeTime = nextDate;
                                flag = 1;
                                break;
                            }
                        }
                        if (0 == flag) {
                            executeTime = "";
                        }
                    }
                    if (StringUtils.isEmpty(executeTime)) {
                        executeTime = scheDateList.get(0);
                    }
                }
                if (StringUtils.isEmpty(executeAddress)) {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                } else {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
                }
                smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, executeTime);

                // 调用通用短信接口
                String phoneNo = ptVo.getPhoneNo();
                String smsType = (String) smsTemplMapOut.get("templateType");
                /*
                 * String retMsg =
                 * bizInterClient.call(Constants.SERVICE_NAME_FOR_SMSSEND, "",
                 * inPmap); logger.warn("retMsg==" + retMsg); //
                 * retMsg:{"success":true,"msg":"短信已发送!","data":null,"total":
                 * null} JSONObject jsonResult = (JSONObject)
                 * JSONObject.parse(retMsg); String sendStatus =
                 * Constants.PT_INFORM_SMS_STATUS_SUCCESS; if
                 * (!jsonResult.getBooleanValue("success")) { sendStatus =
                 * Constants.PT_INFORM_SMS_STATUS_FAILED;
                 * logger.error("调用短信发送接口失败：" + jsonResult.getString("msg")); }
                 */
                Map<String, Object> msgMapIn = new HashMap<String, Object>();
                msgMapIn.put("authorId", authorId);
                msgMapIn.put("updateTime", updateTime);
                msgMapIn.put("phoneNo", phoneNo);
                msgMapIn.put("ptSchemeid", ptSchemeid);
                msgMapIn.put("queueId", queueId);
                msgMapIn.put("taskId", taskId);
                msgMapIn.put("updateAccept", updateAccept);
                msgMapIn.put("userId", userId);
                msgMapIn.put("ruleType", ruleType);
                msgMapIn.put("tenantId",tenantId);
                msgMapIn.put("taskName", taskName);
                // 改为调用内部接口
                HashMap<String, Object> outMsgMap = patientqueryCardService.addsendPromptSmsImpl(phoneNo, smsType,
                        smsText, msgMapIn);
                logger.warn("短信发送结果：" + outMsgMap.toString());

                // 更新短信发送次数
                taskMap.put("smsNumIncr", 1);
                upret = patientTaskDao.updatePatientTaskById(taskMap);
                if (0 == upret) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
                }
                // pt_task_info_his历史轨迹变化记录
                patientTaskDao.insertPatientTaskHisById(taskMap);
            } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(ruleType)) {
                // 邮件提醒
                informTypeBuffer.append("邮件通知、");
            } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(ruleType)) {
                // 微信提醒
                informTypeBuffer.append("微信通知、");
            } else {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
            }
        }
        String infmsg = informTypeBuffer.toString().substring(0, informTypeBuffer.length() - 1);

        // 操作日志记录（ia_operation_record）
        if (Constants.OP_CODE_3002.equals(opCode)) {
            // 后台任务处理
            remarkBuffer.append("后台为患者【");
            remarkBuffer.append(ptVo.getUserName());
            remarkBuffer.append("】发送【");
            remarkBuffer.append(taskName);
            remarkBuffer.append("】门诊任务【");
            remarkBuffer.append(infmsg);
            remarkBuffer.append("】提醒");
            remark = remarkBuffer.toString();
        }
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        paramRecord.put("userName", ptVo.getUserName());
        paramRecord.put("userId", userId);
        paramRecord.put("queueId", queueId);
        paramRecord.put("authorId", authorId);
        paramRecord.put("opTime", updateTime);
        paramRecord.put("type", type);
        paramRecord.put("opUsertype", opUsertype);
        paramRecord.put("authorName", authorName);
        paramRecord.put("opCode", opCode);
        paramRecord.put("updateAccept", updateAccept);
        paramRecord.put("tenantId", tenantId);
        paramRecord.put("remark", remark);
        paramRecord.put("opType", opType);
        paramRecord.put("taskId", taskId);
        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
        patientOperationRecordDao.saveOperationRecord(outRecord);

        return;
    }

 // 窗口期任务患者发送随访提醒短信接口实现
    @Transactional
    public void dealPatientWinTaskSmsSendNew(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String tenantId = (String) argMap.get("tenantId");

        String fuInformid = (String) argMap.get("ruleId");
        String scheduleFlag = (String) argMap.get("scheduleFlag");

        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }

        String type = "";
        String opUsertype = "";
        String remark = "";
        String authorName = "";
        String executeAddress = "";
        String executeTime = "";
        String informType = "";
        StringBuffer remarkBuffer = new StringBuffer();
        if (Constants.OP_CODE_3002.equals(opCode)) {
            // 后台任务处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_21;
            opUsertype = Constants.OPCODE_TYPE_BG_ADMIN;
        } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
            // 前台门诊处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_10;
            opUsertype = Constants.OPCODE_TYPE_DOCTOR;
            remark = (String) argMap.get("opNote");
            authorName = (String) argMap.get("authorName");
            executeAddress = (String) argMap.get("orderAddress");
            executeTime = (String) argMap.get("orderDate");
            informType = (String) argMap.get("informType");
        }

        Map<String, Object> taskMap = new HashMap<String, Object>();
        // 先锁表，防止并发更新
        taskMap.put("updateAuthor", authorId);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        taskMap.put("taskId", taskId);
        int upret = patientTaskDao.updatePatientTaskById(taskMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 校验患者队列状态，非正常不发送短信
        Map<String, Object> userMap = new HashMap<String, Object>();
        userMap.put("userId", userId);
        List<PatientQueueScheme> patientQueueSchemes = patientQueueSchemeDao.selectByUserId(userMap);
        if (!Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL.equals(patientQueueSchemes.get(0).getUserStatus())) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_STATUS_NOT_NORMAL,
                    "患者在该队列中状态异常，无法进行相关操作！");
        }

        // 校验任务状态
        List<PatientTask> ptList = patientTaskDao.selectById(taskMap);
        if (CollectionUtils.isEmpty(ptList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }
        PatientTask ptask = ptList.get(0);
        String taskStatus = ptask.getTaskStatus();
        if (!Constants.PATIENT_TASK_STATUS_INIT.equals(taskStatus)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASKSTATUS_NOT_INIT,
                    "患者任务状态非未随访，不需要发送随访短信！"));
        }

        // 查询短信提醒规则配置
        Map<String, Object> ruleMapIn = new HashMap<String, Object>();
        ruleMapIn.put("queueId", queueId);
        ruleMapIn.put("fuInformid", fuInformid);
        ruleMapIn.put("ruleType", Constants.FU_INFORM_RULE_SMS_SEND_RULE_TYPE_WIN);// 窗口期任务类型
        if (!StringUtils.isEmpty(informType)) {
            ruleMapIn.put("informType", informType);
        }
        ruleMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        List<FuInformruleDict> informRuleList = confCacheDao.getRuleByQueueId(ruleMapIn);
        if (CollectionUtils.isEmpty(informRuleList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULE_NOT_EXIST,
                    "该队列未配置随访提醒规则或开关未放开，请联系系统管理员！"));
        }
        FuInformruleDict fifdVo = new FuInformruleDict();
        String currDateTime = DateUtil.getSysDateA();
        Map<String, Object> smsMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapOut = new HashMap<String, Object>();
        StringBuffer informTypeBuffer = new StringBuffer();
//        for (pp = 0; pp < rulesize; pp++) {
            fifdVo = informRuleList.get(0);

            smsMapIn.clear();
            smsTemplMapIn.clear();
            smsTemplMapOut.clear();
            smsTemplMapIn.put("id", fifdVo.getSmsTemplid());
            smsTemplMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
            smsTemplMapOut = smsTemplDao.selectSmsTemplateById(smsTemplMapIn);
            if (null == smsTemplMapOut || 0 == smsTemplMapOut.size()) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_NOT_EXIST,
                        "对应短信模板未配置或开关未放开，请联系系统管理员！"));
            }
            // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
            String ruleType = fifdVo.getInformType();
            if (Constants.FU_INFORM_RULE_TYPE_APP.equals(ruleType)) {
                // app提醒
                informTypeBuffer.append("app通知、");
            } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(ruleType)) {
                // 短信提醒
                informTypeBuffer.append("短信通知、");
                float cycleTime = 0;
                if (Constants.OP_CODE_3002.equals(opCode)) {
                    cycleTime = fifdVo.getCycleTime();// 单位：小时，后台自动发送短信规则，如一周发送一次
                } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
                    cycleTime = fifdVo.getOutcycleTime();// 单位：小时，医生门诊发送短信规则，如半小时发送一次
                }
                String rangeTime = DateUtil.calcDayByHour(currDateTime, Constants.SUBTRACT_CHAR, cycleTime);
                smsMapIn.put("taskId", taskId);
                smsMapIn.put("rangeTime", rangeTime);
                smsMapIn.put("smsType", smsTemplMapOut.get("templateType"));
                smsMapIn.put("ruleId",fifdVo.getId());
//                smsMapIn.put("sendStatus", Constants.PT_INFORM_SMS_STATUS_SUCCESS);
                // 校验当前周期内是否有已经发了提醒短信
                int cnt = ptSmsRdDao.countSmsRdByConditionNew(smsMapIn);
                if (cnt > 0) {
                    if (cycleTime * Constants.ONE_HOUR_MINUTE == Constants.FU_INFORM_TIME_INTERVAL_HALF_HOUR) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "提醒短信发送过于密集，请于半小时后再试！"));
                    } else if ((int) cycleTime >= Constants.ONE_DAY_HOUR) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "当前周期内已发送过随访提醒短信！"));
                    } else {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "提醒短信发送过于密集，请于" + (int) cycleTime + "小时后再试！"));
                    }
                }

                // 校验最大发送次数(当前窗口期内允许发送的最大提醒次数)
                if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                    if (ptask.getSmsNum() >= fifdVo.getLimitNum()) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                                "患者当前窗口期内随访短信已超过最大发送次数，不允许再发送！"));
                    }
                }

                // 替换短信模板中的变量
                StringBuilder smsContent = new StringBuilder();
                String smsTempl = (String) smsTemplMapOut.get("smsTemplate");
                if (StringUtils.isEmpty(smsTempl)) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
                }
                if (StringUtils.isEmpty(smsTemplMapOut.get("sign").toString())) {
                    // 没定义签名 用默认 或者不签名
                    smsContent.append("");
                } else {
                    smsContent.append(smsTemplMapOut.get("sign").toString());
                }
                smsContent.append(smsTempl);
                /*
                 * 短信模板样例：
                 * 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您于${followTime}到本院进行门诊随访${
                 * followAddress }。
                 * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
                 */
                String smsText = smsContent.toString();
                if (Constants.OP_CODE_3002.equals(opCode)) {
                    String deptId = "";
                    Map<String, Object> queueMapIn = new HashMap<String, Object>();
                    queueMapIn.put("id", queueId);
                    List<FollowQueue> fqList = queueDao.selectById(queueMapIn);
                    if (!CollectionUtils.isEmpty(fqList)) {
                        FollowQueue fqVo = fqList.get(0);
                        deptId = fqVo.getDeptId();
                        if (StringUtils.isEmpty(deptId)) {
                            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_FOR_DEPT_NOT_EXIST,
                                    "队列对应科室id为空!"));
                        }
                    } else {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_QUEUE_NOT_EXIST, "任务对应队列不存在!"));
                    }
                    if("01".equals(scheduleFlag)){
	               		 if (!StringUtils.isEmpty(ptask.getExecuteAddress())) {
	                            executeAddress = ptask.getExecuteAddress();
	                            executeTime = ptask.getExecuteTime();
	                        } else {
	                            // 从科室配置表中获取地址，若有多个地址，则不选用(因为不清楚医生会在哪个地址门诊)
	                            Map<String, Object> deptConMap = new HashMap<String, Object>();
	                            deptConMap.put("deptId", deptId);
	                            List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
	                            if (!CollectionUtils.isEmpty(dcList)) {
	                                if (1 == dcList.size()) {
	                                    executeAddress = dcList.get(0).getAddress();
	                                }
	                            }
	                        }

	                        // 若执行时间不为空，则判断当前时间是否小于执行时间，小于则使用，否则不使用;
	                        String nowDate = DateUtil.getCurrDate2();
	                        if (!StringUtils.isEmpty(executeTime)) {
	                            if (nowDate.compareTo(executeTime) >= 0) {
	                                executeTime = "";
	                            }
	                        }

	                        // 重新查询排班（从明天开始），获取具体时间段(上午，下午或晚上等）
	                        Map<String, Object> scheMapIn = new HashMap<String, Object>();
	                        scheMapIn.put("deptId", deptId);
	                        scheMapIn.put("beginTime", DateUtil.getCurrDateByDay2(1, Constants.PLUS_CHAR));
	                        scheMapIn.put("endTime", ptask.getEndTime());
	                        List<String> scheDateList = listOutPtRandSchedule(scheMapIn);
	                        if (CollectionUtils.isEmpty(scheDateList)) {
	                            throw (new AppException(ErrorCode.DEPARTMENT_EXCEPTION_TYPE_OUTPATIENT_SCHEDULE_ISNULL,
	                                    "截止到任务临期时间，无随访排班！"));
	                        }

	                        // 判断执行时间是否是排班中的一个
	                        if (!StringUtils.isEmpty(executeTime)) {
	                            int kt = 0;
	                            int ksize = scheDateList.size();
	                            String nextDate = "";
	                            int flag = 0;
	                            for (kt = 0; kt < ksize; kt++) {
	                                nextDate = scheDateList.get(kt);
	                                String[] nextDateArr = nextDate.split(Constants.COLON_SPLIT_STR);
	                                if (executeTime.equals(nextDateArr[0])) {
	                                    executeTime = nextDate;
	                                    flag = 1;
	                                    break;
	                                }
	                            }
	                            if (0 == flag) {
	                                executeTime = "";
	                            }
	                        }
	                        if (StringUtils.isEmpty(executeTime)) {
	                            executeTime = scheDateList.get(0);
	                        }
	                        smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, executeTime);
	                        if (StringUtils.isEmpty(executeAddress)) {
	                            smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
	                        } else {
	                            smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
	                        }
	               	}
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, executeTime);
                }

                // 调用通用短信接口
                String phoneNo = ptVo.getPhoneNo();
                String smsType = (String) smsTemplMapOut.get("templateType");

                Map<String, Object> msgMapIn = new HashMap<String, Object>();
                msgMapIn.put("authorId", authorId);
                msgMapIn.put("updateTime", updateTime);
                msgMapIn.put("phoneNo", phoneNo);
                msgMapIn.put("ptSchemeid", ptSchemeid);
                msgMapIn.put("queueId", queueId);
                msgMapIn.put("taskId", taskId);
                msgMapIn.put("updateAccept", updateAccept);
                msgMapIn.put("userId", userId);
                msgMapIn.put("ruleType", ruleType);
                msgMapIn.put("taskName", taskName);
                msgMapIn.put("tenantId",tenantId);

                msgMapIn.put("smsType", smsType);
                msgMapIn.put("ruleId", fifdVo.getId());
                msgMapIn.put("smsText", smsText);
                // 改为调用内部接口
                smsService.saveSmsInfo(msgMapIn);
                logger.warn("短信入实时短信发表成功");

                // 更新短信发送次数
                taskMap.put("smsNumIncr", 1);
                upret = patientTaskDao.updatePatientTaskById(taskMap);
                if (0 == upret) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
                }
            } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(ruleType)) {
                // 邮件提醒
                informTypeBuffer.append("邮件通知、");
            } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(ruleType)) {
                // 微信提醒
                informTypeBuffer.append("微信通知、");
            } else {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
            }
//        }

        try{
        	// pt_task_info_his历史轨迹变化记录
            patientTaskDao.insertPatientTaskHisById(taskMap);
	        String infmsg = informTypeBuffer.toString().substring(0, informTypeBuffer.length() - 1);

	        // 操作日志记录（ia_operation_record）
	        if (Constants.OP_CODE_3002.equals(opCode)) {
	            // 后台任务处理
	            remarkBuffer.append("后台为患者【");
	            remarkBuffer.append(ptVo.getUserName());
	            remarkBuffer.append("】发送【");
	            remarkBuffer.append(taskName);
	            remarkBuffer.append("】门诊任务【");
	            remarkBuffer.append(infmsg);
	            remarkBuffer.append("】提醒");
	            remark = remarkBuffer.toString();
	        }
	        Map<String, Object> paramRecord = new HashMap<String, Object>();
	        paramRecord.put("userName", ptVo.getUserName());
	        paramRecord.put("userId", userId);
	        paramRecord.put("queueId", queueId);
	        paramRecord.put("authorId", authorId);
	        paramRecord.put("opTime", updateTime);
	        paramRecord.put("type", type);
	        paramRecord.put("opUsertype", opUsertype);
	        paramRecord.put("authorName", authorName);
	        paramRecord.put("opCode", opCode);
	        paramRecord.put("updateAccept", updateAccept);
	        paramRecord.put("tenantId", tenantId);
	        paramRecord.put("remark", remark);
	        paramRecord.put("opType", opType);
	        paramRecord.put("taskId", taskId);
	        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
	        patientOperationRecordDao.saveOperationRecord(outRecord);
        }catch(Exception e){
        	logger.error("后台发送短信 操作历史入表失败 taskId="+taskId, e);
        }
        return;
    }

 // 窗口期任务患者发送随访提醒短信接口实现
    @Transactional
    public void dealPatientWinTaskSmsSendByDoctor(Map<String, Object> servParamMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String tenantId = (String) argMap.get("tenantId");

        String fuInformid = (String) argMap.get("ruleId");
//        String scheduleFlag = (String) argMap.get("scheduleFlag");

        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }

        String type = "";
        String opUsertype = "";
        String remark = "";
        String authorName = "";
        String executeAddress = "";
        String executeTime = "";
        String informType = "";

        // 前台门诊处理
        type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_10;
        opUsertype = Constants.OPCODE_TYPE_DOCTOR;
        remark = (String) argMap.get("opNote");
        authorName = (String) argMap.get("authorName");
        executeAddress = (String) argMap.get("orderAddress");
        executeTime = (String) argMap.get("orderDate");
        informType = (String) argMap.get("informType");

        Map<String, Object> taskMap = new HashMap<String, Object>();
        // 先锁表，防止并发更新
        taskMap.put("updateAuthor", authorId);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        taskMap.put("taskId", taskId);
        int upret = patientTaskDao.updatePatientTaskById(taskMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 校验患者队列状态，非正常不发送短信
        Map<String, Object> userMap = new HashMap<String, Object>();
        userMap.put("userId", userId);
        List<PatientQueueScheme> patientQueueSchemes = patientQueueSchemeDao.selectByUserId(userMap);
        if (!Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL.equals(patientQueueSchemes.get(0).getUserStatus())) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_STATUS_NOT_NORMAL,
                    "患者在该队列中状态异常，无法进行相关操作！");
        }

        // 校验任务状态
        List<PatientTask> ptList = patientTaskDao.selectById(taskMap);
        if (CollectionUtils.isEmpty(ptList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }
        PatientTask ptask = ptList.get(0);
        String taskStatus = ptask.getTaskStatus();
        if (!Constants.PATIENT_TASK_STATUS_INIT.equals(taskStatus)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASKSTATUS_NOT_INIT,
                    "患者任务状态非未随访，不需要发送随访短信！"));
        }

        // 查询短信提醒规则配置
        Map<String, Object> ruleMapIn = new HashMap<String, Object>();
        ruleMapIn.put("queueId", queueId);
        ruleMapIn.put("fuInformid", fuInformid);
        ruleMapIn.put("ruleType", Constants.FU_INFORM_RULE_SMS_SEND_RULE_TYPE_DOCTOR);// 手动发送类型
        if (!StringUtils.isEmpty(informType)) {
            ruleMapIn.put("informType", informType);
        }
        ruleMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
        List<FuInformruleDict> informRuleList = confCacheDao.getRuleByQueueId(ruleMapIn);
        if (CollectionUtils.isEmpty(informRuleList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULE_NOT_EXIST,
                    "该队列未配置手动随访提醒规则或开关未放开，请联系系统管理员！"));
        }
        FuInformruleDict fifdVo = new FuInformruleDict();
        String currDateTime = DateUtil.getSysDateA();
        Map<String, Object> smsMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapIn = new HashMap<String, Object>();
        Map<String, Object> smsTemplMapOut = new HashMap<String, Object>();
        StringBuffer informTypeBuffer = new StringBuffer();
//        for (pp = 0; pp < rulesize; pp++) {
            fifdVo = informRuleList.get(0);

            String startTime = fifdVo.getBeginTime();
            String endTime = fifdVo.getEndTime();
            String nowTime = DateUtil.getHm();
            if(nowTime.compareTo(startTime)<0 || nowTime.compareTo(endTime)>0){//当前时间小于开始时间 ||当前时间大于 发送截止时间
            	 throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_NOT_IN_TIME_RANGE,
                         "当前时间不在可发送短信时间段内，请联系系统管理员！"));
            }
            smsMapIn.clear();
            smsTemplMapIn.clear();
            smsTemplMapOut.clear();
            smsTemplMapIn.put("id", fifdVo.getSmsTemplid());
            smsTemplMapIn.put("status", Constants.TABLE_RECORD_STATUS_NOMAL);
            smsTemplMapOut = smsTemplDao.selectSmsTemplateById(smsTemplMapIn);
            if (null == smsTemplMapOut || 0 == smsTemplMapOut.size()) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_NOT_EXIST,
                        "对应短信模板未配置或开关未放开，请联系系统管理员！"));
            }
            // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
            String ruleType = fifdVo.getInformType();
            if (Constants.FU_INFORM_RULE_TYPE_APP.equals(ruleType)) {
                // app提醒
                informTypeBuffer.append("app通知、");
            } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(ruleType)) {
                // 短信提醒
                informTypeBuffer.append("短信通知、");
                float cycleTime = 0;
                if (Constants.OP_CODE_3002.equals(opCode)) {
                    cycleTime = fifdVo.getCycleTime();// 单位：小时，后台自动发送短信规则，如一周发送一次
                } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
                    cycleTime = fifdVo.getOutcycleTime();// 单位：小时，医生门诊发送短信规则，如半小时发送一次
                }
                String rangeTime = DateUtil.calcDayByHour(currDateTime, Constants.SUBTRACT_CHAR, cycleTime);
                smsMapIn.put("taskId", taskId);
                smsMapIn.put("rangeTime", rangeTime);
                smsMapIn.put("smsType", smsTemplMapOut.get("templateType"));
                smsMapIn.put("ruleId",fifdVo.getId());
//                smsMapIn.put("sendStatus", Constants.PT_INFORM_SMS_STATUS_SUCCESS);
                // 校验当前周期内是否有已经发了提醒短信
                int cnt = ptSmsRdDao.countSmsRdByConditionNew(smsMapIn);
                if (cnt > 0) {
                    if (cycleTime * Constants.ONE_HOUR_MINUTE == Constants.FU_INFORM_TIME_INTERVAL_HALF_HOUR) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "提醒短信发送过于密集，半小时内只能发送一条提醒短信！"));
                    } else if ((int) cycleTime >= Constants.ONE_DAY_HOUR) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "当前周期内已发送过随访提醒短信！"));
                    } else {
                    	//

                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                                "提醒短信发送过于密集," + (int) cycleTime + "小时内只能发送一条提醒短信"));
                    }
                }

                // 校验最大发送次数(当前窗口期内允许发送的最大提醒次数)
                if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                    if (ptask.getSmsNum() >= fifdVo.getLimitNum()) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                                "患者当前窗口期内随访短信已超过最大发送次数，不允许再发送！"));
                    }
                }

                // 替换短信模板中的变量
                StringBuilder smsContent = new StringBuilder();
                String smsTempl = (String) smsTemplMapOut.get("smsTemplate");
                if (StringUtils.isEmpty(smsTempl)) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
                }
                if (StringUtils.isEmpty(smsTemplMapOut.get("sign").toString())) {
                    // 没定义签名 用默认 或者不签名
                    smsContent.append("");
                } else {
                    smsContent.append(smsTemplMapOut.get("sign").toString());
                }
                smsContent.append(smsTempl);
                /*
                 * 短信模板样例：
                 * 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您于${followTime}到本院进行门诊随访${
                 * followAddress }。
                 * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
                 */
                String smsText = smsContent.toString();


                if (StringUtils.isEmpty(executeAddress)) {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                } else {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
                }

                if (StringUtils.isEmpty(executeTime)) {
                    smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, "");
                } else {
                	smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, executeTime);
                }

                // 调用通用短信接口
                String phoneNo = ptVo.getPhoneNo();
                String smsType = (String) smsTemplMapOut.get("templateType");

                Map<String, Object> msgMapIn = new HashMap<String, Object>();
                msgMapIn.put("authorId", authorId);
                msgMapIn.put("updateTime", updateTime);
                msgMapIn.put("phoneNo", phoneNo);
                msgMapIn.put("ptSchemeid", ptSchemeid);
                msgMapIn.put("queueId", queueId);
                msgMapIn.put("taskId", taskId);
                msgMapIn.put("updateAccept", updateAccept);
                msgMapIn.put("userId", userId);
                msgMapIn.put("ruleType", ruleType);
                msgMapIn.put("taskName", taskName);
                msgMapIn.put("tenantId",tenantId);

                msgMapIn.put("smsType", smsType);
                msgMapIn.put("ruleId", fifdVo.getId());
                msgMapIn.put("smsText", smsText);
                // 改为调用内部接口
                smsService.saveSmsInfo(msgMapIn);
                logger.warn("短信入实时短信发表成功");

                // 更新短信发送次数
                taskMap.put("smsNumIncr", 1);
                upret = patientTaskDao.updatePatientTaskById(taskMap);
                if (0 == upret) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
                }
            } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(ruleType)) {
                // 邮件提醒
                informTypeBuffer.append("邮件通知、");
            } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(ruleType)) {
                // 微信提醒
                informTypeBuffer.append("微信通知、");
            } else {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
            }
//        }

        try{
        	// pt_task_info_his历史轨迹变化记录
            patientTaskDao.insertPatientTaskHisById(taskMap);

	        Map<String, Object> paramRecord = new HashMap<String, Object>();
	        paramRecord.put("userName", ptVo.getUserName());
	        paramRecord.put("userId", userId);
	        paramRecord.put("queueId", queueId);
	        paramRecord.put("authorId", authorId);
	        paramRecord.put("opTime", updateTime);
	        paramRecord.put("type", type);
	        paramRecord.put("opUsertype", opUsertype);
	        paramRecord.put("authorName", authorName);
	        paramRecord.put("opCode", opCode);
	        paramRecord.put("updateAccept", updateAccept);
	        paramRecord.put("tenantId", tenantId);
	        paramRecord.put("remark", remark);
	        paramRecord.put("opType", opType);
	        paramRecord.put("taskId", taskId);
	        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
	        patientOperationRecordDao.saveOperationRecord(outRecord);
        }catch(Exception e){
        	logger.error("后台发送短信 操作历史入表失败 taskId="+taskId, e);
        }
        return;
    }

    // 通用版获取科室排班接口实现
    public List<String> listOutPtRandSchedule(Map<String, Object> map) throws Exception {
        String deptId = (String) map.get("deptId");
        String beginTime = (String) map.get("beginTime");
        String endTime = (String) map.get("endTime");
        List<String> nextDateList = new ArrayList<String>();

        // 根据科室id获取科室排班
        Map<String, Object> randSchedule = new HashMap<String, Object>();
        randSchedule.put("deptId", deptId);
        List<RandOutPatientSchedule> ropsList = outPatientScheduleDao.listRandOutPatientScheduleByDept(randSchedule);
        if (!CollectionUtils.isEmpty(ropsList)) {
            List<Map<String, Object>> scheduleList = new ArrayList<Map<String, Object>>();
            int kt = 0;
            int dcsize = ropsList.size();
            RandOutPatientSchedule rtVo = new RandOutPatientSchedule();
            for (kt = 0; kt < dcsize; kt++) {
                Map<String, Object> rpMap = new HashMap<String, Object>();
                rtVo = ropsList.get(kt);
                rpMap.put("workDay", rtVo.getWorkDay());
                rpMap.put("workPeriod", rtVo.getWorkPeriod());
                scheduleList.add(rpMap);
            }

            Map<String, Object> calcMap = new HashMap<String, Object>();
            calcMap.put("scheduleType", ropsList.get(0).getScheduleType());// 一个科室只有一种类型的排班
            calcMap.put("scheduleList", scheduleList);
            calcMap.put("beginTime", beginTime);
            calcMap.put("endTime", endTime);
            // 计算下次门诊的时间
            nextDateList = ToolUtils.calcNextDate(calcMap);
        } else {
            throw (new AppException(ErrorCode.DEPARTMENT_EXCEPTION_TYPE_OUTPATIENT_SCHEDULE_ISNULL,
                    "科室无随访排班，请先到系统设置中排班！"));
        }

        return nextDateList;
    }

    // 患者随访提醒记录查询接口实现
    public HashMap<String, Object> listPtInformRd(Map<String, Object> paramMap) throws Exception {
        HashMap<String, Object> argMap = new HashMap<String, Object>(paramMap);
        String tenantId = (String) argMap.get("tenantId");
        int total = 0;
        HashMap<String, Object> retMap = new HashMap<String, Object>();
        List<Map<String, Object>> pqsList = new ArrayList<Map<String, Object>>();
        retMap.put("total", 0);
        retMap.put("rows", pqsList);

        // 若有服务号码或患者姓名，则先获取患者id串（可能有多个）
        String userName = (String) argMap.get("userName");// 可以是姓氏或姓名
        if (null != userName && !"".equals(userName)) {
            argMap.put("userName", userName + "%");
        }

        // 根据当前登录账号获取，其可以查看的队列id（权限处理比较复杂，这里先简化）
        String authorId = (String) argMap.get("authorId");
        HashMap<String, Object> qryQueueMap = new HashMap<String, Object>();
        qryQueueMap.put("authorId", authorId);
        qryQueueMap.put("queueStatus", Constants.QUEUE_STATUS_NOMAL);// 队列状态正常
        qryQueueMap.put("tenantId", tenantId);
        qryQueueMap.put("queueTypePrivate", Constants.QUEUE_TYPE_PRIVATE);
        qryQueueMap.put("queueTypeTenant", Constants.QUEUE_TYPE_TENANT);
        qryQueueMap.put("queueTypePublic", Constants.QUEUE_TYPE_PUBLIC);

        List<FollowQueue> fqList = null;
        // 1：如果传入队列 则直接用队列查询 2：没有传入队列ID 则根据权限先查询 该用户有哪些队列权限 再查询
        String queueId = (String) argMap.get("queueId");
        if (!StringUtils.isEmpty(queueId)) {
            Map<String, Object> queueMap = new HashMap<String, Object>();
            queueMap.put("id", queueId);
            fqList = queueDao.selectById(queueMap);
        } else {
            fqList = queueDao.selectQueueIdByAuthority(qryQueueMap);
            //根据authorId查询此用户拥有哪些队列的权限
            if (ObjectUtil.isNotEmpty(fqList)){
                List<String> queueIds = followQueueAuthorityDAO.getDuiLieByAuthorId(authorId);
                fqList = fqList.stream().filter(item -> {
                    if (queueIds.contains(item.getId())){
                        return true;
                    }else {
                        return false;
                    }
                }).collect(Collectors.toList());
            }
        }

        if (null == fqList || 0 == fqList.size()) {
            logger.warn("该账号无可查看到的队列信息...arg==" + qryQueueMap.toString());
            return retMap;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.select(Constants.REDIS_DB_INDEX_CONFIG);

            List<String> queueIdArray = new ArrayList<String>();
            int fqt = 0;
            for (; fqt < fqList.size(); fqt++) {
                FollowQueue fqVo = fqList.get(fqt);
                // 后续sql中用in条件处理（这里先不考虑返回结果超过1000个）
                queueIdArray.add(fqVo.getId());
            }

            // 获取随访患者list
            argMap.put("queueIdIn", queueIdArray);
            argMap.put("userStatus", Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL);// 队列中正常状态下的用户
            total = patientQueueSchemeDao.countPtSmsRdNumByCondition(argMap);
            if (total > 0) {
                pqsList = patientQueueSchemeDao.listPtSmsRdNumByCondition(argMap);
                if (null == pqsList || 0 == pqsList.size()) {
                    // 数据被删除或改变了
                    total = 0;
                } else {
                    int pq = 0;
                    String pqsQueueId = "";
                    Map<String, Object> qryMapIn = new HashMap<String, Object>();
                    Map<String, Object> qryMapOut = new HashMap<String, Object>();
                    for (; pq < pqsList.size(); pq++) {
                        Map<String, Object> pqsMap = pqsList.get(pq);

                        // 获取队列名称
                        pqsQueueId = (String) pqsMap.get("followQueueId");
                        qryMapIn.clear();
                        qryMapIn.put("queueId", pqsQueueId);
                        qryMapOut = patientService.getQueueName(qryMapIn, jedis);
                        pqsMap.put("followQueueName", qryMapOut.get("queueName"));

                        pqsMap.put("age", ToolUtils.getAgeMonDayByBirthday("" + pqsMap.get("birthday")));
                        pqsMap.put("pregnancyWeek",
                                ToolUtils.getPregnancyStr(Integer.parseInt("" + pqsMap.get("pregnancyDay"))));
                    }
                }
            }
        } catch (JedisConnectionException jex) {
            logger.error("redis连接异常..." + jex.getMessage());
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_ERROR, "Redis缓存连接失败!"));
        } finally {
            if (null != jedis) {
                try {
                    jedis.close();
                    logger.warn("close redis connection...");
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_CLOSE_FAILED,
                            "Redis缓存关闭失败!"));
                }
            }
        }

        retMap.put("total", total);
        retMap.put("rows", pqsList);

        return retMap;
    }

    // 临期患者记录查询接口实现
    public HashMap<String, Object> listPtWarnQueue(Map<String, Object> paramMap) throws Exception {
        HashMap<String, Object> argMap = new HashMap<String, Object>(paramMap);
        String tenantId = (String) argMap.get("tenantId");
        int total = 0;
        HashMap<String, Object> retMap = new HashMap<String, Object>();
        List<Map<String, Object>> pqsList = new ArrayList<Map<String, Object>>();
        retMap.put("total", 0);
        retMap.put("rows", pqsList);

        // 若有服务号码或患者姓名，则先获取患者id串（可能有多个）
        List<String> userIdArray = new ArrayList<String>();
        int qryUserFlag = 0;
        String phoneNo = (String) argMap.get("phoneNo");
        String userName = (String) argMap.get("userName");// 可以是姓氏或姓名
        HashMap<String, Object> qryUserMap = new HashMap<String, Object>();
        if (null != phoneNo && !"".equals(phoneNo)) {
            qryUserMap.put("phoneNo", phoneNo);
            qryUserMap.put("tenantId", tenantId);
            qryUserFlag = 1;
        }
        if (null != userName && !"".equals(userName)) {
            argMap.put("userName", userName + "%");
        }
        if (1 == qryUserFlag) {
            List<Patient> ptList = patientDao.select(qryUserMap);
            if (null == ptList || 0 == ptList.size()) {
                logger.warn("无此患者信息...arg==" + qryUserMap.toString());
                return retMap;
            }
            int ipt = 0;
            for (; ipt < ptList.size(); ipt++) {
                Patient ptVo = ptList.get(ipt);
                // 后续sql中用in条件处理（这里先不考虑返回结果超过1000个）
                userIdArray.add(ptVo.getId());
            }
        }

        // 根据当前登录账号获取，其可以查看的队列id（权限处理比较复杂，这里先简化）
        String authorId = (String) argMap.get("authorId");
        HashMap<String, Object> qryQueueMap = new HashMap<String, Object>();
        qryQueueMap.put("authorId", authorId);
        qryQueueMap.put("queueStatus", Constants.QUEUE_STATUS_NOMAL);// 队列状态正常
        qryQueueMap.put("tenantId", tenantId);
        qryQueueMap.put("queueTypePrivate", Constants.QUEUE_TYPE_PRIVATE);
        qryQueueMap.put("queueTypeTenant", Constants.QUEUE_TYPE_TENANT);
        qryQueueMap.put("queueTypePublic", Constants.QUEUE_TYPE_PUBLIC);

        List<FollowQueue> fqList = null;
        // 1：如果传入队列 则直接用队列查询 2：没有传入队列ID 则根据权限先查询 该用户有哪些队列权限 再查询
        String queueId = (String) argMap.get("queueId");
        if (!StringUtils.isEmpty(queueId)) {
            Map<String, Object> queueMap = new HashMap<String, Object>();
            queueMap.put("id", queueId); // 租户id先写死
            fqList = queueDao.selectById(queueMap);
        } else {
            fqList = queueDao.selectQueueIdByAuthority(qryQueueMap);
        }

        if (null == fqList || 0 == fqList.size()) {
            logger.warn("该账号无可查看到的队列信息...arg==" + qryQueueMap.toString());
            return retMap;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.select(Constants.REDIS_DB_INDEX_CONFIG);
            List<String> queueIdArray = new ArrayList<String>();
            int fqt = 0;
            for (; fqt < fqList.size(); fqt++) {
                FollowQueue fqVo = fqList.get(fqt);
                // 后续sql中用in条件处理（这里先不考虑返回结果超过1000个）
                queueIdArray.add(fqVo.getId());
            }

            // 获取随访患者list
            argMap.put("queueIdIn", queueIdArray);
            if (0 != userIdArray.size()) {
                argMap.put("userIdIn", userIdArray);
            }
            argMap.put("userStatus", Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL);// 队列中正常状态下的用户
            argMap.put("taskStatus", Constants.PATIENT_TASK_STATUS_INIT);// 未开始的任务
            argMap.put("currDate", DateUtil.getCurrDate());
            argMap.put("initTaskRev", "0");
            argMap.put("taskType", Constants.TASK_TYPE_SCHEME_SCHEDULE);// 门诊任务

            total = patientQueueSchemeDao.countPtWarnQueueByCondition(argMap);
            if (total > 0) {
                pqsList = patientQueueSchemeDao.listPtWarnQueueByCondition(argMap);
                if (null == pqsList || 0 == pqsList.size()) {
                    // 数据被删除或改变了
                    total = 0;
                } else {
                    int pq = 0;
                    String pqsQueueId = "";
                    Map<String, Object> qryMapIn = new HashMap<String, Object>();
                    Map<String, Object> qryMapOut = new HashMap<String, Object>();
                    for (; pq < pqsList.size(); pq++) {
                        Map<String, Object> pqsMap = pqsList.get(pq);

                        // 获取队列名称
                        pqsQueueId = (String) pqsMap.get("followQueueId");
                        qryMapIn.clear();
                        qryMapIn.put("queueId", pqsQueueId);
                        qryMapOut = patientService.getQueueName(qryMapIn, jedis);
                        pqsMap.put("followQueueName", qryMapOut.get("queueName"));

                        // 获取方案名称
                        qryMapIn.clear();
                        qryMapIn.put("schemeId", (String) pqsMap.get("schemeId"));
                        qryMapOut = patientService.getSchemeName(qryMapIn, jedis);
                        pqsMap.put("schemeName", qryMapOut.get("schemeName"));

                        pqsMap.put("createTime", DateUtil.getTimeStampDay(pqsMap.get("createTime").toString()));

                     // 年龄
                        Object birthdayObj = (Object) pqsMap.get("birthday");
                        if (null != birthdayObj) {
                            String birthdayStr = String.valueOf(birthdayObj);
                            if (!StringUtils.isEmpty("" + pqsMap.get("pregnancyDay"))
                                    && !StringUtils.isEmpty(birthdayStr)) {
                            	String pregnancy = "" + pqsMap.get("pregnancyDay");
                                long correctAge = ToolUtils.getCorrect(String.valueOf(pqsMap.get("birthday")),
                                        Integer.parseInt(pregnancy));

                                Date date = new Date(correctAge);
                                pqsMap.put("age", ToolUtils.getAgeByBirthday(DateUtil.getDate_8(date)));
                                pqsMap.put("pregnancyWeek",
                                        ToolUtils.getPregnancyStr(Integer.parseInt("" + pqsMap.get("pregnancyDay"))));
                            } else if (!StringUtils.isEmpty(birthdayStr)) {
                                // 孕周为空则按正常年龄算
                                pqsMap.put("age", ToolUtils.getAgeByBirthday("" + pqsMap.get("birthday")));
                            } else {
                                pqsMap.put("age", "");
                            }
                        } else {
                            pqsMap.put("age", "");
                        }

                    }
                }
            }
        } catch (JedisConnectionException jex) {
            logger.error("redis连接异常..." + jex.getMessage());
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_ERROR, "Redis缓存连接失败!"));
        } finally {
            if (null != jedis) {
                try {
                    jedis.close();
                    logger.warn("close redis connection...");
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_CLOSE_FAILED,
                            "Redis缓存关闭失败!"));
                }
            }
        }

        retMap.put("total", total);
        retMap.put("rows", pqsList);

        return retMap;
    }

    // 患者随访提醒门诊地址及排班计划查询接口实现
    public HashMap<String, Object> getPtScheduleInfo(Map<String, Object> paramMap) throws Exception {
        HashMap<String, Object> argMap = new HashMap<String, Object>(paramMap);
        HashMap<String, Object> retMap = new HashMap<String, Object>();
        String deptId = (String) argMap.get("deptId");
        String patientId = (String) argMap.get("patientId");
        String taskId = (String) argMap.get("taskId");
        String tenantId = (String) argMap.get("tenantId");

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", patientId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 校验任务状态
        Map<String, Object> taskMap = new HashMap<String, Object>();
        taskMap.put("taskId", taskId);
        List<PatientTask> ptList = patientTaskDao.selectById(taskMap);
        if (CollectionUtils.isEmpty(ptList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }
        PatientTask ptask = ptList.get(0);
        String taskStatus = ptask.getTaskStatus();
        if (!Constants.PATIENT_TASK_STATUS_INIT.equals(taskStatus)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASKSTATUS_NOT_INIT,
                    "患者任务状态非未随访，不需要发送随访短信！"));
        }

        // 如果当期随访任务中的执行地址不为空，则使用该地址作为随访地址
        List<String> addressList = new ArrayList<String>();
        String executeAddress = "";
        String executeTime = "";
        if (!StringUtils.isEmpty(ptask.getExecuteAddress())) {
            executeAddress = ptask.getExecuteAddress();
            executeTime = ptask.getExecuteTime();
            addressList.add(executeAddress);
        }
        // 从科室配置表中获取地址，前台展示让医生选择，pt_task_info中的executeAddress不为空则展示为第一个
        Map<String, Object> deptConMap = new HashMap<String, Object>();
        deptConMap.put("deptId", deptId);
        if (!StringUtils.isEmpty(executeAddress)) {
            deptConMap.put("revAddress", executeAddress);
        }
        List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
        if (!CollectionUtils.isEmpty(dcList)) {
            int dcSize = dcList.size();
            int dc = 0;
            Contact deptConVo = new Contact();
            for (dc = 0; dc < dcSize; dc++) {
                deptConVo = dcList.get(dc);
                addressList.add(deptConVo.getAddress());
            }
        }

        // 若执行时间不为空，则判断当前时间是否小于执行时间，小于则使用，否则不使用;
        String nowDate = DateUtil.getCurrDate2();
        if (!StringUtils.isEmpty(executeTime)) {
            if (nowDate.compareTo(executeTime) > 0) {
                executeTime = "";
            }
        }
        // 重新查询排班（从今天开始），获取具体时间段(上午，下午或晚上等）
        Map<String, Object> scheMapIn = new HashMap<String, Object>();
        scheMapIn.put("deptId", deptId);
        scheMapIn.put("beginTime", DateUtil.getCurrDate2());
        scheMapIn.put("endTime", ptask.getEndTime());
        List<String> scheDateList = listOutPtRandSchedule(scheMapIn);
        if (CollectionUtils.isEmpty(scheDateList)) {
            throw (new AppException(ErrorCode.DEPARTMENT_EXCEPTION_TYPE_OUTPATIENT_SCHEDULE_ISNULL,
                    "截止到当前任务过期时间，科室无随访排班，无法发送提醒！"));
        }
        // 将executeTime作为首选，若在门诊排班日期内
        List<String> dateList = new ArrayList<String>();
        if (!StringUtils.isEmpty(executeTime)) {
            int kt = 0;
            int ksize = scheDateList.size();
            for (kt = 0; kt < ksize; kt++) {
                String nextDate = scheDateList.get(kt);
                String[] nextDateArr = nextDate.split(Constants.COLON_SPLIT_STR);
                if (executeTime.equals(nextDateArr[0])) {
                    executeTime = nextDate;
                    dateList.add(nextDate);
                    break;
                }
            }
            for (kt = 0; kt < ksize; kt++) {
                String nextDate = scheDateList.get(kt);
                if (executeTime.equals(nextDate)) {
                    continue;
                }
                dateList.add(executeTime);
            }
        } else {
            dateList = scheDateList;
        }

        retMap.put("outScheduleList", dateList);
        retMap.put("addressList", addressList);

        return retMap;
    }

    // 从队列中删除患者接口实现
    @Transactional
    public Map<String, Object> removePtInfoFromQueue(Map<String, Object> paramMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(paramMap);
        Map<String, Object> outMap = new HashMap<String, Object>();

        String authorName = (String) argMap.get("authorName");
        String updateTime = (String) argMap.get("updateTime");
        String authorId = (String) argMap.get("authorId");
        String updateAccept = (String) argMap.get("updateAccept");
        String tenantId = (String) argMap.get("tenantId");
        String opCode = (String) argMap.get("opCode");
        String ptSchemeId = (String) argMap.get("ptSchemeId");
        String opNote = (String) argMap.get("opNote");
        String opType = (String) argMap.get("opType");
        String userId = (String) argMap.get("userId");
        String queueId = (String) argMap.get("queueId");

        // 查询队列实例表记录
        Map<String, Object> ptQueueIn = new HashMap<String, Object>();
        ptQueueIn.put("id", ptSchemeId);
        List<PatientQueueScheme> ptQueueList = patientQueueSchemeDao.selectPatientByCondition(ptQueueIn);
        if (CollectionUtils.isEmpty(ptQueueList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_NOT_EXIST, "患者队列实例无记录！"));
        }
        PatientQueueScheme pqsVo = ptQueueList.get(0);
        // 患者已删除
        if (Constants.PATIENT_QUEUESCHEME_USER_STATUS_DELETE.equals(pqsVo.getUserStatus())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_STATUS_HAS_REMOVED,
                    "该患者在该队列中已删除，无法重复删除！"));
        }

        // 从队列实例表中更新该患者状态为失效（删除）
        ptQueueIn.put("updateTime", updateTime);
        ptQueueIn.put("updateAccept", updateAccept);
        ptQueueIn.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        ptQueueIn.put("userStatus", Constants.PATIENT_QUEUESCHEME_USER_STATUS_DELETE);
        ptQueueIn.put("updateAuthor", authorId);
        int upret = patientQueueSchemeDao.updatePtQueueSchemeRecord(ptQueueIn);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_NOT_EXIST, "患者队列实例无记录！"));
        }
        patientQueueSchemeDao.insertPatientQueueSchemeHis(ptQueueIn);

        // 入操作流水记录表 ia_operation_record
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        Map<String, Object> ptInMap = new HashMap<String, Object>();
        ptInMap.put("userId", userId);
        ptInMap.put("tenantId", tenantId);
        Patient ptOut = patientDao.selectById(ptInMap);
        if (null == ptOut || "".equals(ptOut.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询无此患者记录！"));
        }
        paramRecord.put("userName", ptOut.getUserName());
        paramRecord.put("userId", userId);
        paramRecord.put("queueId", queueId);
        paramRecord.put("authorId", authorId);
        paramRecord.put("opTime", updateTime);
        paramRecord.put("type", Constants.DOCTOR_ACTIVE_BUSI_TYPE_10);
        paramRecord.put("opUsertype", Constants.OPCODE_TYPE_DOCTOR);
        paramRecord.put("authorName", authorName);
        paramRecord.put("opCode", opCode);
        paramRecord.put("updateAccept", updateAccept);
        paramRecord.put("tenantId", tenantId);
        paramRecord.put("remark", opNote);
        paramRecord.put("opType", opType);
        Map<String, Object> outRecord = buildOperationRecord(paramRecord);
        patientOperationRecordDao.saveOperationRecord(outRecord);

        return outMap;
    }

    // 给患者添加临时任务接口实现
    @Override
    @Transactional
    public Map<String, Object> saveNewTempPatientTask(Map<String, Object> paramMap) throws Exception {
        Map<String, Object> argMap = new HashMap<String, Object>(paramMap);
        Map<String, Object> outMap = new HashMap<String, Object>();

        Map<String, Object> sessionInfoMap = ToolUtils.typeCast(argMap.get("sessionInfo"));
        Map<String, Object> oprInfoMap = ToolUtils.typeCast(argMap.get("oprInfo"));
        Map<String, Object> taskInfoMap = ToolUtils.typeCast(argMap.get("taskInfo"));

        // sessionInfo
        String authorName = (String) sessionInfoMap.get("authorName");
        String authorId = (String) sessionInfoMap.get("authorId");
        String tenantId = (String) sessionInfoMap.get("tenantId");
        // oprInfo
        String updateTime = (String) oprInfoMap.get("updateTime");
        String updateAccept = (String) oprInfoMap.get("updateAccept");
        String opCode = (String) oprInfoMap.get("opCode");
        String opType = (String) oprInfoMap.get("opType");
        String opNote = (String) oprInfoMap.get("opNote");
        // taskInfo
        String taskId = (String) taskInfoMap.get("ptTaskId");
        String queueId = (String) taskInfoMap.get("followQueueId");
        String ptSchemeId = (String) taskInfoMap.get("ptSchemeId");
        String userId = (String) taskInfoMap.get("patientId");
        final String executeTime= taskInfoMap.get("executeTime").toString();
        final String actualAge  = taskInfoMap.get("actualAge").toString();

        // 查询当前任务信息
        Map<String, Object> taskMapIn = new HashMap<String, Object>();
        taskMapIn.put("taskId", taskId);
        List<PatientTask> ptVoList = patientTaskDao.selectById(taskMapIn);
        if (CollectionUtils.isEmpty(ptVoList)) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_NOT_EXIST, "该患者当前任务不存在！");
        }
        PatientTask currTaskVo = ptVoList.get(0);
        int currSeqNo = currTaskVo.getSeqNo();
        String nextTaskId = currTaskVo.getNextTaskId();
        //获取当前taskid的触发时间，用于后面比较 20180112
        String currExecuteTime = currTaskVo.getExecuteTime();
        //下一次随访的触发时间
        String nextExecuteTime = "";
        // 需要兼容安医大老数据，即next_task_id为空，但有后续随访
        if (StringUtils.isEmpty(nextTaskId)) {
            Map<String, Object> qryTaskIn = new HashMap<String, Object>();
            qryTaskIn.put("ptSchemeId", ptSchemeId);
            qryTaskIn.put("seqNo", currSeqNo + 1);
            qryTaskIn.put("userId", userId);
            List<PatientTask> ptTaskList = patientTaskDao.select(qryTaskIn);
            if (!CollectionUtils.isEmpty(ptTaskList)) {
                PatientTask nextTaskVo = ptTaskList.get(0) ;
                nextTaskId = nextTaskVo.getId();
                //获取下一次随访的触发时间
                nextExecuteTime = nextTaskVo.getExecuteTime();
            }
        }else {
        	//需要查询到当前任务的触发时间用于比较
        	Map<String, Object> qryTaskIn = new HashMap<String, Object>();
            qryTaskIn.put("ptSchemeId", ptSchemeId);
            qryTaskIn.put("followTaskId", nextTaskId);
            qryTaskIn.put("userId", userId);
            List<PatientTask> ptTaskList = patientTaskDao.select(qryTaskIn);
            if(!CollectionUtils.isEmpty(ptTaskList)){
            	PatientTask nextTaskVo = ptTaskList.get(0);
                //获取下一次随访的触发时间
                nextExecuteTime = nextTaskVo.getExecuteTime();
            }
        }
        String qryTaskId = nextTaskId;
        //获取当前时间
        String NowDate = DateUtil.getDate_8(new Date());
        logger.info("currExecuteTime : "+ currExecuteTime +"   nextExecuteTime : "+nextExecuteTime);
        if(!StringUtils.isEmpty(currExecuteTime) && !StringUtils.isEmpty(nextExecuteTime)) {
        	//获取当前时间和当前任务的时间
        	int day = Math.abs(DateUtil.daysBetweenStr(currExecuteTime, NowDate));
        	//获取当前时间和下次任务的时间
        	int dayTow = Math.abs(DateUtil.daysBetweenStr(nextExecuteTime, NowDate));
        	logger.info("day : "+ day +"   dayTow : "+dayTow);
        	//判断两个时间差的大小，如果day>dayTow 那么临时的任务为当前任务，反之为下次任务
        	if(currSeqNo==0) {
        		qryTaskId = nextTaskId;
        	}else {
        		if(day>dayTow) {
	        		qryTaskId = nextTaskId;
	        	}else {
	        		qryTaskId = taskId;
	        	}
        	}
        }
        logger.info("qryTaskId : "+ qryTaskId);
        if (StringUtils.isEmpty(qryTaskId)) {
            if (Constants.TASK_SEQ_FOR_INIT == currSeqNo) {
                // 当前为建档任务(即该方案下只配置了建档任务)，则无法创建新的临时任务
                throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_CANNOT_ADD_TEMP_TASK,
                        "该患者只配置了建档任务，无法添加临时任务！");
            }
            // 下次任务id为空，则选择当前任务id
            qryTaskId = taskId;
        }

        // 查询任务信息
        taskMapIn.clear();
        taskMapIn.put("taskId", qryTaskId);
        List<PatientTask> qryPtTaskList = patientTaskDao.selectById(taskMapIn);
        if (CollectionUtils.isEmpty(qryPtTaskList)) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_NOT_EXIST,
                    "该患者【" + qryTaskId + "】任务ID对应任务不存在！");
        }
        PatientTask refPtTaskVo = qryPtTaskList.get(0);
        String currDay = DateUtil.getCurrDate2();
        String ptk_id = SeqUtil.getSeqNo(Constants.SEQ_NO_FOR_PTTASKID);

        // 更新当前任务的next_task_id为新生成的临时任务id
        Map<String, Object> taskMap = new HashMap<String, Object>();
        taskMap.put("taskId", taskId);
        taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        taskMap.put("nextTaskId", ptk_id);
        int upret = patientTaskDao.updatePatientTaskById(taskMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }
        patientTaskDao.insertPatientTaskHisById(taskMap);
        taskMap.clear();

        // 下次任务不为空，则更新后续所有的seq_no=seq_no+1
        if (!StringUtils.isEmpty(nextTaskId)) {
            taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
            taskMap.put("updateTime", updateTime);
            taskMap.put("updateAccept", updateAccept);
            taskMap.put("ptSchemeId", refPtTaskVo.getPtSchemeid());
            taskMap.put("seqNoGt", currTaskVo.getSeqNo() + 1);
            taskMap.put("seqNoIncr", "1");
            taskMap.put("taskType", Constants.TASK_TYPE_SCHEME_SCHEDULE);// 门诊任务
            patientTaskDao.updatePtTaskByPtSchemeId(taskMap);
            patientTaskDao.insertPtTaskHisByPtSchemeId(taskMap);
        }

        // 新增临时任务
        PatientTask tmpPtkVo = new PatientTask();
        tmpPtkVo.setId(ptk_id);
        tmpPtkVo.setNextTaskId(nextTaskId);// 将原有的next_task_id更新到临时任务上，可能为空(在最后一次任务之后添加临时任务)
        tmpPtkVo.setTaskStatus(Constants.PATIENT_TASK_STATUS_INIT);
        tmpPtkVo.setAuthorId(authorId);
//        tmpPtkVo.setExecuteTime(currDay);
        tmpPtkVo.setExecuteTime(executeTime);
        tmpPtkVo.setBeginTime(currDay);
        tmpPtkVo.setEndTime(currDay);
        tmpPtkVo.setCreateTime(updateTime);
        tmpPtkVo.setExecuteDay(Constants.TASK_EXECUTEDAY_FOR_TEMP_TASK);
        tmpPtkVo.setExecuteType(refPtTaskVo.getExecuteType());
        tmpPtkVo.setInformFlag(refPtTaskVo.getInformFlag());
        tmpPtkVo.setPtSchemeid(refPtTaskVo.getPtSchemeid());
        tmpPtkVo.setSeqNo(currTaskVo.getSeqNo() + 1);
        tmpPtkVo.setTaskIntro(Constants.TASK_NAME_INTRO_FOR_TMP_TASK);
//        tmpPtkVo.setTaskName(Constants.TASK_NAME_INTRO_FOR_TMP_TASK);
        tmpPtkVo.setTaskName(Constants.TASK_NAME_INTRO_FOR_TMP_TASK + "（" + actualAge + "）" );
        tmpPtkVo.setUpdateTime(updateTime);
        tmpPtkVo.setUserId(refPtTaskVo.getUserId());
        tmpPtkVo.setWarnDay(Constants.TASK_WARNDAY_FOR_TEMP_TASK);
        tmpPtkVo.setWindowDay(Constants.TASK_WINDOWDAY_FOR_TEMP_TASK);
        tmpPtkVo.setQueueId(queueId);
        tmpPtkVo.setUpdateAccept(updateAccept);
        tmpPtkVo.setTaskType(refPtTaskVo.getTaskType());
        tmpPtkVo.setAuditStatus(Constants.PATIENT_TASK_AUDIT_STATUS_INIT);// 初始化审核状态
        tmpPtkVo.setTemporaryTask(Constants.PATIENT_TASK_TEMPORARY_TASK);
        patientTaskDao.save(tmpPtkVo);

        // 入任务历史表
        taskMap.clear();
        taskMap.put("taskId", ptk_id);
        taskMap.put("updateType", Constants.UPDATE_TYPE_INSERT);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        patientTaskDao.insertPatientTaskHisById(taskMap);

        // 新增任务内容
        taskMap.clear();
        taskMap.put("taskId", refPtTaskVo.getId());
        List<PatientTaskContent> refPtTaskCntList = patientTaskConDao.selectContentByTaskId(taskMap);
        if (!CollectionUtils.isEmpty(refPtTaskCntList)) {
            List<PatientTaskContent> ptTaskContentList = new ArrayList<PatientTaskContent>();
            int cntSize = refPtTaskCntList.size();
            int kk = 0;
            for (kk = 0; kk < cntSize; kk++) {
                PatientTaskContent refPtcVo = refPtTaskCntList.get(kk);
                PatientTaskContent ptc = new PatientTaskContent();
                ptc.setCreateTime(updateTime);
                ptc.setUpdateTime(updateTime);
                ptc.setId(SeqUtil.getSeqNo(Constants.SEQ_NO_FOR_PTTASK_CONTID));
                ptc.setTaskId(ptk_id);
                ptc.setTemplVersionid(refPtcVo.getTemplVersionid());
                ptc.setUpdateAccept(updateAccept);
                ptc.setTemplType(refPtcVo.getTemplType());
                ptc.setUserId(refPtcVo.getUserId());
                ptc.setPtSchemeid(refPtcVo.getPtSchemeid());
                ptc.setSeqNo(refPtcVo.getSeqNo());

                ptTaskContentList.add(ptc);
            }
            if (ptTaskContentList.size() > 0) {
                patientTaskConDao.savePatientTaskContentBatch(ptTaskContentList);
            }
        }

        // 更新队列实例记录，总任务数加1
        Map<String, Object> ptQueueMap = new HashMap<String, Object>();
        ptQueueMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        ptQueueMap.put("updateTime", updateTime);
        ptQueueMap.put("updateAccept", updateAccept);
        ptQueueMap.put("id", ptSchemeId);
        ptQueueMap.put("updateAuthor", authorId);
        ptQueueMap.put("totalNumIncr", "1");
        upret = patientQueueSchemeDao.updatePtQueueSchemeRecord(ptQueueMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_NOT_EXIST, "患者队列实例无记录！"));
        }
        patientQueueSchemeDao.insertPatientQueueSchemeHis(ptQueueMap);

        // 患者信息
        Map<String, Object> userInMap = new HashMap<String, Object>();
        userInMap.put("userId", userId);
        userInMap.put("tenantId", tenantId);
        Patient ptOut = patientDao.selectById(userInMap);
        if (null == ptOut) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询无此患者记录！"));
        }

        // 入操作流水记录表 ia_operation_record
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        paramRecord.put("userName", ptOut.getUserName());
        paramRecord.put("userId", userId);
        paramRecord.put("queueId", queueId);
        paramRecord.put("authorId", authorId);
        paramRecord.put("opTime", updateTime);
        if (Constants.OP_TYPE_SELF.equals(opType)) {
            paramRecord.put("type", Constants.PATIENT_ACTIVE_BUSI_TYPE_8);
        } else {
            paramRecord.put("type", Constants.PATIENT_ACTIVE_BUSI_TYPE_9);
        }
        if (Constants.OP_TYPE_SELF.equals(opType)) {
            paramRecord.put("opUsertype", Constants.OPCODE_TYPE_PATIENT);
        } else {
            paramRecord.put("opUsertype", Constants.OPCODE_TYPE_DOCTOR);
        }
        paramRecord.put("authorName", authorName);
        paramRecord.put("opCode", opCode);
        paramRecord.put("updateAccept", updateAccept);
        paramRecord.put("tenantId", tenantId);
        paramRecord.put("remark", opNote);
        paramRecord.put("opType", opType);
        paramRecord.put("taskId", ptk_id);
        Map<String, Object> outRecord = this.buildOperationRecord(paramRecord);
        patientOperationRecordDao.saveOperationRecord(outRecord);

        outMap.put("newTaskId", ptk_id);

        return outMap;
    }

    /**
     * 修改临时任务（任务名称、计划随访日期）。
     * @param paramMap
     * @return
     * @throws Exception
     */
    @Override
    @Transactional
    public Map<String, Object> updateTempTask(final Map<String, Object> paramMap) throws Exception {
        Map<String, Object> argMap  = new HashMap<String, Object>(paramMap);
        Map<String, Object> retMap  = new HashMap<String, Object>();
        final String taskId = argMap.get("taskId").toString();
        final String executeTime= argMap.get("executeTime").toString();
        final String actualAge  = argMap.get("actualAge").toString();
        PatientTask vo  = new PatientTask();
        vo.setId(taskId);
        vo.setExecuteTime(executeTime);
        vo.setTaskName(Constants.TASK_NAME_INTRO_FOR_TMP_TASK + "（" + actualAge + "）");
        int updRet  = patientTaskDao.updateTempTask(vo);
        if (1 == updRet)    {
            retMap.put("success", "true");
        } else {
            retMap.put("success", "false");
        }
        return retMap;
    }

    // 删除临时任务接口实现
    @Override
    @Transactional
    public Map<String, Object> delTempPatientTask(Map<String, Object> paramMap) throws Exception {
    	Map<String, Object> argMap = Collections.synchronizedMap(new HashMap<String, Object>(paramMap));
        Map<String, Object> outMap = Collections.synchronizedMap(new HashMap<String, Object>());

        Map<String, Object> sessionInfoMap = Collections.synchronizedMap(new HashMap<String, Object>());
        sessionInfoMap = ToolUtils.typeCast(argMap.get("sessionInfo"));
        Map<String, Object> oprInfoMap = Collections.synchronizedMap(new HashMap<String, Object>());
        oprInfoMap = ToolUtils.typeCast(argMap.get("oprInfo"));
        Map<String, Object> taskInfoMap = Collections.synchronizedMap(new HashMap<String, Object>());
        taskInfoMap = ToolUtils.typeCast(argMap.get("taskInfo"));

        // sessionInfo
        String authorName = (String) sessionInfoMap.get("authorName");
        String authorId = (String) sessionInfoMap.get("authorId");
        String tenantId = (String) sessionInfoMap.get("tenantId");
        // oprInfo
        String updateTime = (String) oprInfoMap.get("updateTime");
        String updateAccept = (String) oprInfoMap.get("updateAccept");
        String opCode = (String) oprInfoMap.get("opCode");
        String opType = (String) oprInfoMap.get("opType");
        String opNote = (String) oprInfoMap.get("opNote");
        // taskInfo
        String taskId = (String) taskInfoMap.get("ptTaskId");
        String queueId = (String) taskInfoMap.get("followQueueId");
        String ptSchemeId = (String) taskInfoMap.get("ptSchemeId");
        String userId = (String) taskInfoMap.get("patientId");

        // 查询当前要删除任务信息
        Map<String, Object> taskMapIn = Collections.synchronizedMap(new HashMap<String, Object>());
        taskMapIn.put("taskId", taskId);
        List<PatientTask> ptVoList = patientTaskDao.selectById(taskMapIn);
        if (CollectionUtils.isEmpty(ptVoList)) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_NOT_EXIST, "该患者当前任务不存在！");
        }else if(StringUtils.isEmpty(ptVoList.get(0).getTemporaryTask()) || !ptVoList.get(0).getTemporaryTask().equals(Constants.PATIENT_TASK_TEMPORARY_TASK)){
        	throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_NOT_EXIST, "删除失败,当前任务不是随访任务！");
        }
        PatientTask currTaskVo = ptVoList.get(0);//当前任务信息
        //int currSeqNo = currTaskVo.getSeqNo();//序号
        String nextTaskId = currTaskVo.getNextTaskId();//下次任务id
        //修改next_task_id是当前id的任务
        if (StringUtils.isEmpty(nextTaskId)) {//要删除的任务的next_task_id不存在,将所有next_task_id为当前要删除的任务id的记录next_task_id=""
        	Map<String, Object> taskMap = Collections.synchronizedMap(new HashMap<String, Object>());
        	taskMap.put("nextTaskId", taskId);
        	//获取所有next_task_id为当前任务id的数据
        	List<PatientTask> beforList = patientTaskDao.select(taskMap);
        	taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        	taskMap.put("updateTime", updateTime);
            taskMap.put("updateAccept", updateAccept);
            taskMap.put("updateAuthor", authorId);
            taskMap.put("nextTaskId", "");
        	if(!CollectionUtils.isEmpty(beforList)){
        		for (int i = 0; i < beforList.size(); i++) {
            		taskMap.put("taskId", beforList.get(i).getId());
            		int upret = patientTaskDao.updatePatientTaskById(taskMap);
                    if (0 == upret) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
                    }
                    patientTaskDao.insertPatientTaskHisById(taskMap);
    			}
        	}
        }else {//删除任务的下次任务不为空,将所有next_task_id为当前要删除的任务id的记录next_task_id=删除任务的下次任务id
        	Map<String, Object> befor_taskMap = Collections.synchronizedMap(new HashMap<String, Object>());
        	befor_taskMap.put("nextTaskId", taskId);
        	//获取所有next_task_id为当前任务id的数据
        	List<PatientTask> beforList = patientTaskDao.select(befor_taskMap);
        	befor_taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        	befor_taskMap.put("updateTime", updateTime);
        	befor_taskMap.put("updateAccept", updateAccept);
        	befor_taskMap.put("updateAuthor", authorId);
        	befor_taskMap.put("nextTaskId", nextTaskId);
        	if(!CollectionUtils.isEmpty(beforList)){
        		for (int i = 0; i < beforList.size(); i++) {
        			befor_taskMap.put("taskId", beforList.get(i).getId());
            		int upret = patientTaskDao.updatePatientTaskById(befor_taskMap);
                    if (0 == upret) {
                        throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
                    }
                    patientTaskDao.insertPatientTaskHisById(befor_taskMap);
    			}
        	}
        	befor_taskMap.clear();
        	//更新删除任务后续所有的seq_no=seq_no-1
        	befor_taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        	befor_taskMap.put("updateTime", updateTime);
        	befor_taskMap.put("updateAccept", updateAccept);
        	befor_taskMap.put("ptSchemeId", currTaskVo.getPtSchemeid());//方案id
        	befor_taskMap.put("seqNoGt", currTaskVo.getSeqNo() + 1);
        	befor_taskMap.put("seqNoIncr", "1");
        	befor_taskMap.put("taskType", Constants.TASK_TYPE_SCHEME_SCHEDULE);// 门诊任务
            patientTaskDao.updatePtTaskByPtSchemeIdToRMOne(befor_taskMap);
            patientTaskDao.insertPtTaskHisByPtSchemeId(befor_taskMap);
        }
        Map<String, Object> taskMap = Collections.synchronizedMap(new HashMap<String, Object>());
        // 删除记录入任务历史表

        taskMap.put("taskId", currTaskVo.getId());
        taskMap.put("updateType", Constants.UPDATE_TYPE_DELETE);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        patientTaskDao.insertPatientTaskHisById(taskMap);
        //删除临时任务
        taskMap.clear();
        taskMap.put("taskId", currTaskVo.getId());
        patientTaskDao.removePtTaskptTaskById(taskMap);
        //查询任务内容
        List<PatientContentData> ptTaskConList = patientTaskConDao.selectContentDataByTaskId(taskMap);
        if (!CollectionUtils.isEmpty(ptTaskConList)) {
        	//删除任务内容
        	patientTaskConDao.deletePtContentDataInfoByTaskId(currTaskVo.getId());
        	for (int i = 0; i < ptTaskConList.size(); i++) {
        		ptTaskConList.get(i).setUpdateType(Constants.UPDATE_TYPE_DELETE);
        		ptTaskConList.get(i).setUpdateAccept(updateAccept);//操作流水
        		ptTaskConList.get(i).setUpdateTime(updateTime);
			}
        	//批量插入删除记录
        	patientTaskConDao.savePatientTaskContentDataHisBatch(ptTaskConList);
        }
        // 更新队列实例记录，总任务数减1
        Map<String, Object> ptQueueMap = new HashMap<String, Object>();
        ptQueueMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        ptQueueMap.put("updateTime", updateTime);
        ptQueueMap.put("updateAccept", updateAccept);
        ptQueueMap.put("id", ptSchemeId);
        ptQueueMap.put("updateAuthor", authorId);
        ptQueueMap.put("totalNumIncr", "1");
        int upret = patientQueueSchemeDao.updatetotalNumIncrForDelTask(ptQueueMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_NOT_EXIST, "患者队列实例无记录！"));
        }
        patientQueueSchemeDao.insertPatientQueueSchemeHis(ptQueueMap);

        // 患者信息
        Map<String, Object> userInMap = new HashMap<String, Object>();
        userInMap.put("userId", userId);
        userInMap.put("tenantId", tenantId);
        Patient ptOut = patientDao.selectById(userInMap);
        if (null == ptOut) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询无此患者记录！"));
        }

        // 入操作流水记录表 ia_operation_record
        Map<String, Object> paramRecord = new HashMap<String, Object>();
        paramRecord.put("userName", ptOut.getUserName());
        paramRecord.put("userId", userId);
        paramRecord.put("queueId", queueId);
        paramRecord.put("authorId", authorId);
        paramRecord.put("opTime", updateTime);
        if (Constants.OP_TYPE_SELF.equals(opType)) {
            paramRecord.put("type", Constants.PATIENT_ACTIVE_BUSI_TYPE_8);
        } else {
            paramRecord.put("type", Constants.PATIENT_ACTIVE_BUSI_TYPE_9);
        }
        if (Constants.OP_TYPE_SELF.equals(opType)) {
            paramRecord.put("opUsertype", Constants.OPCODE_TYPE_PATIENT);
        } else {
            paramRecord.put("opUsertype", Constants.OPCODE_TYPE_DOCTOR);
        }
        paramRecord.put("authorName", authorName);
        paramRecord.put("opCode", opCode);
        paramRecord.put("updateAccept", updateAccept);
        paramRecord.put("tenantId", tenantId);
        paramRecord.put("remark", opNote);
        paramRecord.put("opType", opType);
        paramRecord.put("taskId", currTaskVo.getId());
        Map<String, Object> outRecord = this.buildOperationRecord(paramRecord);
        patientOperationRecordDao.saveOperationRecord(outRecord);
        outMap.put("newTaskId", currTaskVo.getId());

        return outMap;
    }

	@Override
	public List<PatientTask> getPatientFollowTimeList(
			Map<String, Object> servParamMap) throws Exception {
		 Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);
	        List<PatientTask> ptWinList = patientTaskDao.listPtFollowUPTimeTask(argMap);
	        return ptWinList;
	}

	@Override
	public List<PtTrTask> getPatientNextFollowTimeList(
			Map<String, Object> servParamMap) throws Exception {
		 Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);
	        List<PtTrTask> ptWinList = patientTaskDao.listPtNextFollowTimeTask(argMap);
	        return ptWinList;
	}

	@Override
	public void dealPatientFollowTimeSmsSend(Map<String, Object> servParamMap)
			throws Exception {
		Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String ruleId = (String) argMap.get("ruleId");
        String tenantId = (String) argMap.get("tenantId");
        String deptId = (String)argMap.get("deptId");
        String sign = (String)argMap.get("sign");
        String smsTempl = (String) argMap.get("tempateContent");
        String informType =  (String)argMap.get("informType");
//        String rangeTime = (String)argMap.get("rangeTime");
        String smsType = (String)argMap.get("smsType");
        int beforeDay = (int)argMap.get("beforeDay");
        String executeTime = (String) argMap.get("executeTime");
        String scheduleFlag =(String) argMap.get("scheduleFlag");

        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }

        String type = "";
        String opUsertype = "";
        String remark = "";
        String authorName = "";
        String executeAddress = "";
//        String executeTime = "";
        StringBuffer remarkBuffer = new StringBuffer();
        if (Constants.OP_CODE_3002.equals(opCode)) {
            // 后台任务处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_21;
            opUsertype = Constants.OPCODE_TYPE_BG_ADMIN;
        } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
            // 前台门诊处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_10;
            opUsertype = Constants.OPCODE_TYPE_DOCTOR;
            remark = (String) argMap.get("opNote");
            authorName = (String) argMap.get("authorName");
            executeAddress = (String) argMap.get("orderAddress");
            executeTime = (String) argMap.get("orderDate");
        }

        Map<String, Object> taskMap = new HashMap<String, Object>();

        // 先锁表，防止并发更新
        taskMap.put("updateAuthor", authorId);
        taskMap.put("updateTime", updateTime);
        taskMap.put("updateAccept", updateAccept);
        taskMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
        taskMap.put("taskId", taskId);
        int upret = patientTaskDao.updatePatientTaskById(taskMap);
        if (0 == upret) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 校验患者队列状态，非正常不发送短信
        Map<String, Object> userMap = new HashMap<String, Object>();
        userMap.put("userId", userId);
        List<PatientQueueScheme> patientQueueSchemes = patientQueueSchemeDao.selectByUserId(userMap);
        if (!Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL.equals(patientQueueSchemes.get(0).getUserStatus())) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_STATUS_NOT_NORMAL,
                    "患者在该队列中状态异常，无法进行相关操作！");
        }

        // 校验任务状态
        List<PatientTask> ptList = patientTaskDao.selectById(taskMap);
        if (CollectionUtils.isEmpty(ptList)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
        }

        PatientTask ptask = ptList.get(0);
        String taskStatus = ptask.getTaskStatus();
        if (!Constants.PATIENT_TASK_STATUS_INIT.equals(taskStatus)) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASKSTATUS_NOT_INIT,
                    "患者任务状态非未随访，不需要发送随访短信！"));
        }

        // 查询短信提醒规则配置  前面传过来
        StringBuffer informTypeBuffer = new StringBuffer();

        // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
        if (Constants.FU_INFORM_RULE_TYPE_APP.equals(informType)) {
            // app提醒
            informTypeBuffer.append("app通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(informType) || null == informType) { //默认为短信
            // 短信提醒
            informTypeBuffer.append("短信通知、");
            Map<String,Object> smsMapIn = new HashMap<String,Object>();
            smsMapIn.put("taskId", taskId);
//            smsMapIn.put("rangeTime", rangeTime);
//            smsMapIn.put("sendStatus", Constants.PT_INFORM_SMS_STATUS_SUCCESS);
            smsMapIn.put("ruleId", ruleId);
            smsMapIn.put("smsType", smsType);

            //
            String sendTime = "";
            Date executeDate = DateUtil.getDate_8(executeTime);
            sendTime = DateUtil.getDate_8(DateUtil.subDay(executeDate, beforeDay));
            smsMapIn.put("sendTime",sendTime);

            // 校验本次随访时间 前一周 或 前一天 是否已经发过提醒短信  (包括医生手动发送)
            int cnt = ptSmsRdDao.countSmsHisdByFollowTime(smsMapIn);
            if (cnt > 0) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "当前周期内已发送过随访提醒短信！"));
            }

            // 校验最大发送次数(单次随访允许发送的最大提醒次数)
            /*if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                smsMapIn.remove("rangeTime");
                cnt = ptSmsRdDao.countSmsRdByPtSchemeId(smsMapIn);
                if (cnt >= fifdVo.getLimitNum()) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                            "患者随访短信已超过最大发送次数，不允许再发送！"));
                }
            }*/

            // 替换短信模板中的变量
            StringBuilder smsContent = new StringBuilder();
            if (StringUtils.isEmpty(smsTempl)) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
            }
            if (StringUtils.isEmpty(sign)) {
                // 没定义签名 用默认 或者不签名
                smsContent.append("");
            } else {
                smsContent.append(sign);
            }
            smsContent.append(smsTempl);
            /*
             * 短信模板样例：
             * 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您于${followTime}到本院进行门诊随访${
             * followAddress }。
             * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
             */
            String smsText = smsContent.toString();
            if (Constants.OP_CODE_3002.equals(opCode)) {
            	//判断是否根据排班计划 拼随访时间和 随访低点  默认不需要发送
            	if("01".equals(scheduleFlag)){
            		 if (!StringUtils.isEmpty(ptask.getExecuteAddress())) {
                         executeAddress = ptask.getExecuteAddress();
                         executeTime = ptask.getExecuteTime();
                     } else {
                         // 从科室配置表中获取地址，若有多个地址，则不选用(因为不清楚医生会在哪个地址门诊)
                         Map<String, Object> deptConMap = new HashMap<String, Object>();
                         deptConMap.put("deptId", deptId);
                         List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
                         if (!CollectionUtils.isEmpty(dcList)) {
                             if (1 == dcList.size()) {
                                 executeAddress = dcList.get(0).getAddress();
                             }
                         }
                     }

                     // 若执行时间不为空，则判断当前时间是否小于执行时间，小于则使用，否则不使用;
                     String nowDate = DateUtil.getCurrDate2();
                     if (!StringUtils.isEmpty(executeTime)) {
                         if (nowDate.compareTo(executeTime) >= 0) {
                             executeTime = "";
                         }
                     }

                     // 重新查询排班（从明天开始），获取具体时间段(上午，下午或晚上等）
                     Map<String, Object> scheMapIn = new HashMap<String, Object>();
                     scheMapIn.put("deptId", deptId);
                     scheMapIn.put("beginTime", DateUtil.getCurrDateByDay2(1, Constants.PLUS_CHAR));
                     scheMapIn.put("endTime", ptask.getEndTime());
                     List<String> scheDateList = listOutPtRandSchedule(scheMapIn);
                     if (CollectionUtils.isEmpty(scheDateList)) {
                         throw (new AppException(ErrorCode.DEPARTMENT_EXCEPTION_TYPE_OUTPATIENT_SCHEDULE_ISNULL,
                                 "截止到任务临期时间，无随访排班！"));
                     }

                     // 判断执行时间是否是排班中的一个
                     if (!StringUtils.isEmpty(executeTime)) {
                         int kt = 0;
                         int ksize = scheDateList.size();
                         String nextDate = "";
                         int flag = 0;
                         for (kt = 0; kt < ksize; kt++) {
                             nextDate = scheDateList.get(kt);
                             String[] nextDateArr = nextDate.split(Constants.COLON_SPLIT_STR);
                             if (executeTime.equals(nextDateArr[0])) {
                                 executeTime = nextDate;
                                 flag = 1;
                                 break;
                             }
                         }
                         if (0 == flag) {
                             executeTime = "";
                         }
                     }

                     if (StringUtils.isEmpty(executeTime)) {
                         executeTime = scheDateList.get(0);
                     }
                     smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, executeTime);
                     if (StringUtils.isEmpty(executeAddress)) {
                         smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                     } else {
                         smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
                     }
            	}else{
            		smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
            		smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME,"");
            	}
            }

            // 调用通用短信接口
            String phoneNo = ptVo.getPhoneNo();

            Map<String, Object> msgMapIn = new HashMap<String, Object>();
            msgMapIn.put("authorId", authorId);
            msgMapIn.put("updateTime", updateTime);
            msgMapIn.put("phoneNo", phoneNo);
            msgMapIn.put("ptSchemeid", ptSchemeid);
            msgMapIn.put("queueId", queueId);
            msgMapIn.put("taskId", taskId);
            msgMapIn.put("updateAccept", updateAccept);
            msgMapIn.put("userId", userId);
            msgMapIn.put("ruleType", informType);
            msgMapIn.put("taskName", taskName);
            msgMapIn.put("tenantId",tenantId);
            msgMapIn.put("ruleId", ruleId);

            msgMapIn.put("smsType", smsType);
            msgMapIn.put("smsText", smsText);
            // 改为调用内部接口
            smsService.saveSmsInfo(msgMapIn);
            logger.warn("短信入实时短信发表成功");

            // 更新短信发送次数
            taskMap.put("smsNumIncr", 1);
            upret = patientTaskDao.updatePatientTaskById(taskMap);
            if (0 == upret) {
            	logger.error("后台发送短信 更新任务表短信发送记录失败"+taskId);
                //throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
            }
        } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(informType)) {
            // 邮件提醒
            informTypeBuffer.append("邮件通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(informType)) {
            // 微信提醒
            informTypeBuffer.append("微信通知、");
        } else {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
        }

        try{
        	// pt_task_info_his历史轨迹变化记录
            patientTaskDao.insertPatientTaskHisById(taskMap);
        	String infmsg = informTypeBuffer.toString().substring(0, informTypeBuffer.length() - 1);
            // 操作日志记录（ia_operation_record）
            if (Constants.OP_CODE_3002.equals(opCode)) {
                // 后台任务处理
                remarkBuffer.append("后台为患者【");
                remarkBuffer.append(ptVo.getUserName());
                remarkBuffer.append("】发送【");
                remarkBuffer.append(taskName);
                remarkBuffer.append("】门诊任务【");
                remarkBuffer.append(infmsg);
                remarkBuffer.append("】提醒");
                remark = remarkBuffer.toString();
            }
            Map<String, Object> paramRecord = new HashMap<String, Object>();
            paramRecord.put("userName", ptVo.getUserName());
            paramRecord.put("userId", userId);
            paramRecord.put("queueId", queueId);
            paramRecord.put("authorId", authorId);
            paramRecord.put("opTime", updateTime);
            paramRecord.put("type", type);
            paramRecord.put("opUsertype", opUsertype);
            paramRecord.put("authorName", authorName);
            paramRecord.put("opCode", opCode);
            paramRecord.put("updateAccept", updateAccept);
            paramRecord.put("tenantId", tenantId);
            paramRecord.put("remark", remark);
            paramRecord.put("opType", opType);
            paramRecord.put("taskId", taskId);
            Map<String, Object> outRecord = buildOperationRecord(paramRecord);
            patientOperationRecordDao.saveOperationRecord(outRecord);
        }catch(Exception e){
        	logger.error("后台发送短信 操作历史入表失败 taskId="+taskId, e);
        }
        return;
	}

	@Override
	public void dealPtNextFollowTimeSmsSend(Map<String, Object> servParamMap)
			throws Exception {
		Map<String, Object> argMap = new HashMap<String, Object>(servParamMap);

//		String id = (String) argMap.get("trTaskId");
        String taskId = (String) argMap.get("taskId");
        String ptSchemeid = (String) argMap.get("ptSchemeId");
        String authorId = (String) argMap.get("authorId");
        String updateTime = (String) argMap.get("updateTime");
        String updateAccept = (String) argMap.get("updateAccept");
        String userId = (String) argMap.get("userId");
        String opCode = (String) argMap.get("opCode");
        String queueId = (String) argMap.get("queueId");
        String taskName = (String) argMap.get("taskName");
        String ruleId = (String) argMap.get("ruleId");
        String tenantId = (String) argMap.get("tenantId");
        String deptId = (String)argMap.get("deptId");
        String sign = (String)argMap.get("sign");
        String smsTempl = (String) argMap.get("tempateContent");
        String informType =  (String)argMap.get("informType");
//        String rangeTime = (String)argMap.get("rangeTime");
        String smsType = (String)argMap.get("smsType");
        int beforeDay = (int)argMap.get("beforeDay");
        String nextTime = (String) argMap.get("executeTime");
        String scheduleFlag =(String) argMap.get("scheduleFlag");
        String nextAddress =(String) argMap.get("nextAddress");


        String opType = "";
        if (!StringUtils.isEmpty(argMap.get("opType"))) {
            opType = (String) argMap.get("opType");
        }

        String type = "";
        String opUsertype = "";
        String remark = "";
        String authorName = "";
        String executeAddress = "";
//        String executeTime = "";
        StringBuffer remarkBuffer = new StringBuffer();
        if (Constants.OP_CODE_3002.equals(opCode)) {
            // 后台任务处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_21;
            opUsertype = Constants.OPCODE_TYPE_BG_ADMIN;
        } else if (Constants.OP_TYPE_DOOR.equals(opType)) {
            // 前台门诊处理
            type = Constants.DOCTOR_ACTIVE_BUSI_TYPE_10;
            opUsertype = Constants.OPCODE_TYPE_DOCTOR;
            remark = (String) argMap.get("opNote");
            authorName = (String) argMap.get("authorName");
            executeAddress = (String) argMap.get("orderAddress");
            nextTime = (String) argMap.get("orderDate");
        }

//        Map<String, Object> taskMap = new HashMap<String, Object>();

        // 校验患者信息是否存在
        Map<String, Object> userMapIn = new HashMap<String, Object>();
        userMapIn.put("userId", userId);
        userMapIn.put("tenantId", tenantId);
        Patient ptVo = patientDao.selectById(userMapIn);
        if (null == ptVo || "".equals(ptVo.getId())) {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_NOT_EXIST, "查询随访系统无此患者！"));
        }

        // 校验患者队列状态，非正常不发送短信
        Map<String, Object> userMap = new HashMap<String, Object>();
        userMap.put("userId", userId);
        List<PatientQueueScheme> patientQueueSchemes = patientQueueSchemeDao.selectByUserId(userMap);
        if (!Constants.PATIENT_QUEUESCHEME_USER_STATUS_NOMAL.equals(patientQueueSchemes.get(0).getUserStatus())) {
            throw new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_QUEUE_STATUS_NOT_NORMAL,
                    "患者在该队列中状态异常，无法进行相关操作！");
        }

        // 查询短信提醒规则配置  前面传过来
        StringBuffer informTypeBuffer = new StringBuffer();

        // 不同的提醒规则，调用不同的接口（目前暂考虑短信提醒，其他后续扩展）
        if (Constants.FU_INFORM_RULE_TYPE_APP.equals(informType)) {
            // app提醒
            informTypeBuffer.append("app通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_SMS.equals(informType) || null == informType) { //默认为短信
            // 短信提醒
            informTypeBuffer.append("短信通知、");
            Map<String,Object> smsMapIn = new HashMap<String,Object>();
            smsMapIn.put("taskId",taskId);
//          smsMapIn.put("rangeTime", rangeTime);
//          smsMapIn.put("sendStatus", Constants.PT_INFORM_SMS_STATUS_SUCCESS);
            smsMapIn.put("ruleId",ruleId);
            smsMapIn.put("smsType",smsType);

            //
            String sendTime = "";
            Date executeDate = DateUtil.getDate_8(nextTime);
            sendTime = DateUtil.getDate_8(DateUtil.subDay(executeDate,beforeDay));
            smsMapIn.put("sendTime",sendTime);

            // 校验本次随访时间 N天前 是否已经发过提醒短信
            int cnt = ptSmsRdDao.countSmsHisdByFollowTime(smsMapIn);
            if (cnt > 0) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_HAS_SENT,
                            "当前周期内已发送过随访提醒短信！"));
            }

            // 校验最大发送次数(单次随访允许发送的最大提醒次数)
            /*if (Constants.FU_INFORM_RULE_SMS_SEND_TIME_INFINITE != fifdVo.getLimitNum()) {
                smsMapIn.remove("rangeTime");
                cnt = ptSmsRdDao.countSmsRdByPtSchemeId(smsMapIn);
                if (cnt >= fifdVo.getLimitNum()) {
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_SFTXDX_LIMITNUM,
                            "患者随访短信已超过最大发送次数，不允许再发送！"));
                }
            }*/

            // 替换短信模板中的变量
            StringBuilder smsContent = new StringBuilder();
            if (StringUtils.isEmpty(smsTempl)) {
                throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_SMS_TEMPL_CONTENT_ISNULL, "随访短信模板内容为空"));
            }
            if (StringUtils.isEmpty(sign)) {
                // 没定义签名 用默认 或者不签名
                smsContent.append("");
            } else {
                smsContent.append(sign);
            }
            smsContent.append(smsTempl);
            /*
             * 短信模板样例：
             * 【xxx医院】家长您好！为本院xxx科宝宝院后更好成长，请您于${followTime}到本院进行门诊随访${
             * followAddress }。
             * BB-care是专为本院提供随访服务平台，如有疑问您可与客服联系，联系方式：400-1650-166.回复TD退订
             */
            String smsText = smsContent.toString();
            if (Constants.OP_CODE_3002.equals(opCode)) {
            	//判断是否根据排班计划 拼随访时间和 随访低点  默认不需要发送
            	if("01".equals(scheduleFlag)){
            		 if (!StringUtils.isEmpty(nextAddress)) {

                     } else {
                         // 从科室配置表中获取地址，若有多个地址，则不选用(因为不清楚医生会在哪个地址门诊)
                         Map<String, Object> deptConMap = new HashMap<String, Object>();
                         deptConMap.put("deptId", deptId);
                         List<Contact> dcList = deptContactDao.listDeptContactByCondition(deptConMap);
                         if (!CollectionUtils.isEmpty(dcList)) {
                             if (1 == dcList.size()) {
                                 executeAddress = dcList.get(0).getAddress();
                             }
                         }
                     }

                     // 若执行时间不为空，则判断当前时间是否小于执行时间，小于则使用，否则不使用;
                     String nowDate = DateUtil.getCurrDate2();
                     if (!StringUtils.isEmpty(nextTime)) {
                         if (nowDate.compareTo(nextTime) >= 0) {
                        	 nextTime = "";
                         }
                     }

                     // 重新查询排班（从明天开始），获取具体时间段(上午，下午或晚上等）
                     Map<String, Object> scheMapIn = new HashMap<String, Object>();
                     scheMapIn.put("deptId", deptId);
                     scheMapIn.put("beginTime", DateUtil.getCurrDateByDay2(1, Constants.PLUS_CHAR));
                     scheMapIn.put("endTime", nextTime); //发短信时间 - 下次随访时间
                     List<String> scheDateList = listOutPtRandSchedule(scheMapIn);
                     if (CollectionUtils.isEmpty(scheDateList)) {
                         throw (new AppException(ErrorCode.DEPARTMENT_EXCEPTION_TYPE_OUTPATIENT_SCHEDULE_ISNULL,
                                 "截止到任务临期时间，无随访排班！"));
                     }

                     // 判断下次随访时间是否是排班中的一个
                     if (!StringUtils.isEmpty(nextTime)) {
                         int kt = 0;
                         int ksize = scheDateList.size();
                         String nextDate = "";
                         int flag = 0;
                         for (kt = 0; kt < ksize; kt++) {
                             nextDate = scheDateList.get(kt);
                             String[] nextDateArr = nextDate.split(Constants.COLON_SPLIT_STR);
                             if (nextTime.equals(nextDateArr[0])) {
                            	 nextTime = nextDate;
                                 flag = 1;
                                 break;
                             }
                         }
                         if (0 == flag) {
                        	 nextTime = "";
                         }
                     }

                     if (StringUtils.isEmpty(nextTime)) {
                    	 nextTime = scheDateList.get(0); //取最近时间的排班计划
                     }
                     smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME, nextTime);
                     if (StringUtils.isEmpty(executeAddress)) {
                         smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "");
                     } else {
                         smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS, "，地址：" + executeAddress + "");
                     }
            	}else{
            		//是否替换排班说明 或者短信中 定死
            		smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_ADDRESS,"");
            		smsText = smsText.replaceAll(Constants.PT_SMS_VAR_FOLLOW_TIME,"");
            	}
            }

            // 调用通用短信接口
            String phoneNo = ptVo.getPhoneNo();

            Map<String, Object> msgMapIn = new HashMap<String, Object>();
            msgMapIn.put("authorId", authorId);
            msgMapIn.put("updateTime", updateTime);
            msgMapIn.put("phoneNo", phoneNo);
            msgMapIn.put("ptSchemeid", ptSchemeid);
            msgMapIn.put("queueId", queueId);
            msgMapIn.put("taskId", taskId);
            msgMapIn.put("updateAccept", updateAccept);
            msgMapIn.put("userId", userId);
            msgMapIn.put("ruleType", informType);
            msgMapIn.put("taskName", taskName);
            msgMapIn.put("tenantId",tenantId);
            msgMapIn.put("ruleId", ruleId);

            msgMapIn.put("smsType", smsType);
            msgMapIn.put("smsText", smsText);
            // 改为调用内部接口
            smsService.saveSmsInfo(msgMapIn);
            logger.warn("短信入实时短信发表成功");

            // 更新短信发送次数
            /*taskMap.put("smsNumIncr", 1);
            int upret = patientTaskDao.updatePatientTaskById(taskMap);
            if (0 == upret) {
            	logger.error("后台发送短信 更新任务表短信发送记录失败"+taskId);
                //throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_PATIENT_TASK_NOT_EXIST, "患者当前任务不存在！"));
            }*/
        } else if (Constants.FU_INFORM_RULE_TYPE_MAIL.equals(informType)) {
            // 邮件提醒
            informTypeBuffer.append("邮件通知、");
        } else if (Constants.FU_INFORM_RULE_TYPE_WECHAT.equals(informType)) {
            // 微信提醒
            informTypeBuffer.append("微信通知、");
        } else {
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_TASK_INFORMRULETYPE_UNKNOWN, "随访提醒类型未定义！"));
        }

        try{
        	// pt_task_info_his历史轨迹变化记录
//            patientTaskDao.insertPatientTaskHisById(taskMap);
        	String infmsg = informTypeBuffer.toString().substring(0, informTypeBuffer.length() - 1);
            // 操作日志记录（ia_operation_record）
            if (Constants.OP_CODE_3002.equals(opCode)) {
                // 后台任务处理
                remarkBuffer.append("后台为患者【");
                remarkBuffer.append(ptVo.getUserName());
                remarkBuffer.append("】发送【");
                remarkBuffer.append(taskName);
                remarkBuffer.append("】门诊任务【");
                remarkBuffer.append(infmsg);
                remarkBuffer.append("】提醒");
                remark = remarkBuffer.toString();
            }
            Map<String, Object> paramRecord = new HashMap<String, Object>();
            paramRecord.put("userName", ptVo.getUserName());
            paramRecord.put("userId", userId);
            paramRecord.put("queueId", queueId);
            paramRecord.put("authorId", authorId);
            paramRecord.put("opTime", updateTime);
            paramRecord.put("type", type);
            paramRecord.put("opUsertype", opUsertype);
            paramRecord.put("authorName", authorName);
            paramRecord.put("opCode", opCode);
            paramRecord.put("updateAccept", updateAccept);
            paramRecord.put("tenantId", tenantId);
            paramRecord.put("remark", remark);
            paramRecord.put("opType", opType);
            paramRecord.put("taskId", taskId);
            Map<String, Object> outRecord = buildOperationRecord(paramRecord);
            patientOperationRecordDao.saveOperationRecord(outRecord);
        }catch(Exception e){
        	logger.error("后台发送短信 操作历史入表失败 taskId="+taskId, e);
        }
        return;
	}
}
